package com.cqrt.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.CollUtil;
import com.hustcad.plm.rpm.model.dto.plan.PlanActiveLockDTO;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.cqrt.config.CustomObjectMapper;
import com.cqrt.config.NameValidator;
import com.cqrt.constant.RTErrorCodeEnum;
import com.cqrt.constant.RTPlmConstant;
import com.cqrt.dto.ExportPlanExcelDTO;
import com.cqrt.dto.TransJobDTO;
import com.cqrt.entity.DeliverableLinkDO;
import com.cqrt.entity.PlanTime;
import com.cqrt.entity.RTPreConstraintDO;
import com.cqrt.entity.RTTraverseParentDO;
import com.cqrt.mapper.*;
import com.cqrt.rt.basic.request.BatchOperationRequest;
import com.cqrt.service.CommonService;
import com.cqrt.service.RTProjectService;
import com.cqrt.util.CompareResult;
import com.cqrt.util.ExcelParser;
import com.cqrt.util.TreeComparatorUtil;
import com.cqrt.util.WorkdayCalculator;
import com.cqrt.vo.CalendarVO;
import com.cqrt.vo.RTExecutorJobVO;
import com.cqrt.vo.RTProjectRelationVO;
import com.cqrt.vo.TyppmPlanActiveVO;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.hustcad.plm.basic.util.TyplmEntityBaseUtil;
import com.hustcad.plm.pdm.common.model.dto.search.SearchConditionDTO;
import com.hustcad.plm.pdm.common.model.dto.search.SearchTypeDTO;
import com.hustcad.plm.pdm.common.model.vo.search.ResultEntityVO;
import com.hustcad.plm.pdm.container.model.dto.search.ProjectEntityParamDTO;
import com.hustcad.plm.pdm.container.model.vo.search.ProjectEntityVO;
import com.hustcad.plm.pdm.core.base.EntityBase;
import com.hustcad.plm.pdm.core.context.TyppmUser;
import com.hustcad.plm.pdm.iba.model.dto.SaveObjIBADTO;
import com.hustcad.plm.pdm.iba.model.dto.TyplmObjOidAndLogicalDTO;
import com.hustcad.plm.pdm.iba.model.dto.TyplmQueryIbaDTO;
import com.hustcad.plm.pdm.iba.service.TyplmAttributeValueService;
import com.hustcad.plm.pdm.type.model.vo.TypeVueVO;
import com.hustcad.plm.pdm.type.service.TyplmTypeService;
import com.hustcad.plm.pdm.user.service.TyOrgExtService;
import com.hustcad.plm.pdm.user.service.TyplmUserService;
import com.hustcad.plm.rpm.constant.ProjectMngConstant;
import com.hustcad.plm.rpm.constant.ProjectMngStateConstant;
import com.hustcad.plm.rpm.entityconvert.BaseConvert;
import com.hustcad.plm.rpm.model.converters.PlanActiveConverter;
import com.hustcad.plm.rpm.model.dto.active.*;
import com.hustcad.plm.rpm.model.dto.admin.TyppmPrjStakeholderDTO;
import com.hustcad.plm.rpm.model.dto.admin.TyppmProjectDTO;
import com.hustcad.plm.rpm.model.dto.calendar.TyppmCalendarDTO;
import com.hustcad.plm.rpm.model.dto.active.BatchLockParams;
import com.hustcad.plm.rpm.model.dto.job.TyppmJobDTO;
import com.hustcad.plm.rpm.model.dto.outrule.TyppmOutRuleDTO;
import com.hustcad.plm.rpm.model.dto.remote.BatchInsertDocVO;
import com.hustcad.plm.rpm.model.dto.remote.BatchRemoteMessageDTO;
import com.hustcad.plm.rpm.model.dto.remote.BatchRemoteMessageParams;
import com.hustcad.plm.rpm.model.dto.remote.DocDetailsVO;
import com.hustcad.plm.rpm.model.dto.remote.RemoteObjDTO;
import com.hustcad.plm.rpm.model.dto.team.ProjectTeamTreeDTO;
import com.hustcad.plm.rpm.model.dto.team.TyppmTeamTreeDTO;
import com.hustcad.plm.rpm.model.entity.admin.TyppmProject;
import com.hustcad.plm.rpm.model.entity.admin.TyppmProjectTemplate;
import com.hustcad.plm.rpm.model.entity.calendar.TyppmCalendar;
import com.hustcad.plm.rpm.model.entity.message.TyppmMessageTemplateDefine;
import com.hustcad.plm.rpm.model.entity.plan.TyppmPlan;
import com.hustcad.plm.rpm.model.entity.plan.TyppmPlanActive;
import com.hustcad.plm.rpm.model.entity.team.TyppmTeamRoleManger;
import com.hustcad.plm.rpm.model.entity.user.TyppmUserEx;
import com.hustcad.plm.rpm.model.vo.admin.ProjectVO;
import com.hustcad.plm.rpm.model.vo.change.ActiveUpdateVO;
import com.hustcad.plm.rpm.model.vo.change.CreateChangeInfoRequestVO;
import com.hustcad.plm.rpm.model.vo.change.PlanChangeInfoVO;
import com.hustcad.plm.rpm.model.vo.change.PlanUpdateVO;
import com.hustcad.plm.rpm.model.vo.job.TyppmJobVO;
import com.hustcad.plm.rpm.model.vo.meta.EnumerationItemTreeVO;
import com.hustcad.plm.rpm.model.vo.outrule.OutRuleItemVO;
import com.hustcad.plm.rpm.model.vo.outrule.OutRuleParamVO;
import com.hustcad.plm.rpm.model.vo.outrule.OutRuleVO;
import com.hustcad.plm.rpm.model.vo.plan.PlanVO;
import com.hustcad.plm.rpm.model.vo.plan.TyppmPlanActivePublishDTO;
import com.hustcad.plm.rpm.project.service.TyppmPrjStakeholderService;
import com.hustcad.plm.rpm.project.service.TyppmProjectService;
import com.hustcad.plm.rpm.service.calendar.TyppmCalendarService;
import com.hustcad.plm.rpm.service.change.TyppmChangeInfoService;
import com.hustcad.plm.rpm.service.dashboard.TyppmDashboardService;
import com.hustcad.plm.rpm.service.forpdm.admin.ProjectServiceForPdm;
import com.hustcad.plm.rpm.service.job.TyppmJobService;
import com.hustcad.plm.rpm.service.message.TyppmMessageTemplateDefineService;
import com.hustcad.plm.rpm.service.org.TyppmUserService;
import com.hustcad.plm.rpm.service.outrule.TyppmOutRuleService;
import com.hustcad.plm.rpm.service.plan.TyppmBaselinePlanActivityService;
import com.hustcad.plm.rpm.service.plan.TyppmGanttChartService;
import com.hustcad.plm.rpm.service.plan.TyppmPlanActiveService;
import com.hustcad.plm.rpm.service.plan.TyppmPlanService;
import com.hustcad.plm.rpm.service.remote.HandlerRemoteService;
import com.hustcad.plm.rpm.service.remote.TyppmRemoteMessageHandle;
import com.hustcad.plm.rpm.service.remote.TyppmRemotePdmService;
import com.hustcad.plm.rpm.service.remoterequest.ThirdRemoteRequestService;
import com.hustcad.plm.rpm.service.resource.TyppmPlanResourceService;
import com.hustcad.plm.rpm.service.team.TyppmContainerTeamService;
import com.hustcad.plm.rpm.service.team.TyppmProjectTeamService;
import com.hustcad.plm.rpm.template.service.TyppmProjectTemplateLibService;
import com.hustcad.plm.rpm.util.beanmapper.BeanMapper;
import com.ty.basic.constant.RoleKeyConstant;
import com.ty.basic.constant.TableTypeConstrant;
import com.ty.basic.dto.constraint.IbaDTO;
import com.cqrt.util.ProjectCodeGenerator;
import com.ty.basic.entity.CTyEntityBaseDO;
import com.ty.basic.entity.IdentifierEntity;
import com.ty.basic.entity.PrincipalEntity;
import com.ty.basic.entity.org.UserDO;
import com.ty.basic.exception.UtilsException;
import com.ty.basic.exception.message.SystemErrorCodeEnum;
import com.ty.basic.exception.message.WfbSystemErrorCodeEnum;
import com.ty.basic.handler.exceptionhandler.exception.ResponseCodeEnum;
import com.ty.basic.handler.exceptionhandler.exception.TyException;
import com.ty.basic.response.ResponseResult;
import com.ty.basic.service.snow.SnowflakeIdComponent;
import com.ty.basic.session.TyAccountContext;
import com.ty.ppm.api.service.admin.ProjectService;
import com.ty.ppm.api.service.plan.PlanActiveOperateService;
import com.ty.ppm.api.service.plan.PlanActiveService;
import com.ty.ppm.api.service.plan.PlanService;
import com.ty.ppm.model.dto.admin.UpdateProjectOwnerDTO;
import com.ty.ppm.model.dto.plan.BaseLinePlanDTO;
import com.ty.ppm.model.entity.admin.Project;
import com.ty.ppm.model.entity.job.Job;
import com.ty.ppm.model.entity.plan.BaselinePlan;
import com.ty.ppm.model.entity.plan.Plan;
import com.ty.ppm.model.entity.plan.PlanActive;
import com.ty.ppm.model.utils.Tools;
import com.ty.ppm.service.service.impl.plan.PlanActiveOperateServiceImpl;
import lombok.extern.slf4j.Slf4j;
import oracle.sql.TIMESTAMP;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static jdk.nashorn.internal.runtime.ScriptRuntime.safeToString;

@Slf4j
@Service
public class RTProjectServiceImpl implements RTProjectService {


    @Value("${main.getibaservice.url}")
    private String mainGetIbaServiceUrl;
    @Value("${tysoft.project.notSpecialChar:*:/\\\\<>?!！：？}")
    private String notSpecialChar;
    @Value("${query.url}")
    private String queryConditionUrl;
    @Resource
    private CommonService commonService;
    @Resource
    private ProjectCodeGenerator projectCodeGenerator;
    @Resource
    private TyppmJobService typpmJobService;
    @Resource
    private RestTemplate restTemplate;
    @Resource
    private RTProjectMapper rtProjectMapper;
    @Resource
    private ThirdRemoteRequestService thirdRemoteRequestService;
    @Resource
    private ProjectServiceForPdm projectServiceForPdm;
    @Resource
    private TyppmGanttChartService ganttChartService;
    @Resource
    private TyppmPlanService typpmPlanService;
    @Resource
    private HandlerRemoteService handlerRemoteService;
    @Resource
    private TyppmPlanActiveService typpmPlanActiveService;
    @Resource
    private RTProjectsTemplateMapper rtProjectsTemplateMapper;
    @Resource
    private TraverseParentMapper traverseParentMapper;
    @Resource
    private RTTaskMapper rtTaskMapper;
    @Resource
    private BaseLinePlanExpandMapper baseLinePlanExpandMapper;
    @Resource
    private TyppmBaselinePlanActivityService baselinePlanActivityService;
    @Resource
    private SnowflakeIdComponent snowflakeIdComponent;
    @Resource
    private TyppmProjectService typpmProjectService;
    @Resource
    private PlanService planService;
    @Resource
    private TyplmAttributeValueService typlmAttributeValueService;
    @Resource
    private TyppmPlanResourceService typpmPlanResourceService;
    @Resource
    private TyplmUserService typlmUserService;
    @Resource
    private TyppmPlanActiveService planActiveService;
    @Resource
    private TyplmTypeService typlmTypeService;
    @Resource
    private TyppmProjectTemplateLibService typpmProjectTemplateLibService;
    @Resource
    private TyOrgExtService orgExtService;
    @Resource
    private TyppmRemotePdmService typpmRemotePdmService;
    @Value("${rt.org.user}")
    private String rtOrgUser;
    @Resource
    private BaseConvert baseConvert;
    @Resource
    private TyppmDashboardService typpmDashboardService;
    @Resource
    private ProjectService projectService;
    @Resource
    private TyppmChangeInfoService typpmChangeInfoService;
    @Resource
    private PlanActiveOperateService planActiveOperateService;
    @Resource
    private DeliverableLinkMapper deliverableLinkMapper;
    @Resource
    private PlanActiveOperateServiceImpl planOperateService;
    @Resource
    private TyppmOutRuleService typpmOutRuleService;
    @Resource
    private PlanActiveService planActiveService2;
    @Resource
    private TyppmContainerTeamService typpmContainerTeamService;
    @Resource
    private PlanActiveConverter planActiveConverter;
    @Value("${tysoft.pdm.pdmMsgSwitch:N}")
    private String pdmMsgSwitch;
    @Value("${tysoft.project.SystemMsg:admin}")
    private String senderUser;
    @Resource
    @Lazy
    private TyppmUserService typpmUserService;
    @Resource
    private TyppmMessageTemplateDefineService typpmMessageTemplateDefineService;
    @Resource
    private TyppmRemoteMessageHandle typpmRemoteMessageHandle;
    @Resource
    private TyppmCalendarService typpmCalendarService;

    @Resource
    private TyppmPrjStakeholderService typpmPrjStakeholderService;


    @Resource
    private TyppmProjectTeamService  typpmProjectTeamService;

    @Override
    public PageInfo<RTExecutorJobVO> queryJobByExecutor(String executorOID, int pageNum, int pageSize) {
        String name = rtProjectMapper.getUserNameById(Tools.toBigInteger(executorOID));
        if (!NameValidator.isNameValid(name)) {
            throw RTErrorCodeEnum.NAME_NOT_VALID.getException("该执行人名称不合法，请重新设置");
        }
        PageHelper.startPage(pageNum, pageSize);
        Map<String, Object> params = new HashMap();
        params.put("executorOID", executorOID);
        params.put("markfordelete", 0L);
        params.put("showTree", "1");
        params.put("orderByEndTime", "true");
        List<String> stateList = Arrays.asList("未读", "已接收", "已驳回");
        params.put("stateList", stateList);
        params.put("queryType", "Executor");
        params.put("shouldPagingQuery", Boolean.TRUE);
        PageInfo<TyppmJobDTO> pageInfo = this.typpmJobService.queryJobPageByParamsChildMark(params, pageNum, pageSize);
        if (pageInfo.getTotal() == 0)
            throw RTErrorCodeEnum.JOB_NOT_EXIST.getException("该执行人当前无未提交任务");
        List<BigInteger> projectIds = pageInfo.getList().stream()
                .map(TyppmJobDTO::getProjectID)
                .map(Tools::toBigInteger)
                .distinct()
                .collect(Collectors.toList());
        Map<BigInteger, String> projectDevelopmentLevels = fetchProjectDevelopmentLevels(projectIds);
        List<RTExecutorJobVO> executorJobVOList = pageInfo.getList().stream()
                .map(job -> {
                    RTExecutorJobVO vo = new RTExecutorJobVO();
                    vo.setOid(Tools.toBigInteger(job.getOid()));
                    vo.setOtype(job.getOtype());
                    vo.setPlanActiveName(job.getPlanActiveName());
                    vo.setProjectName(job.getProjectName());
                    vo.setPlanStartDate(job.getStartdate());
                    vo.setPlanFinishDate(job.getFinishdate());
                    BigInteger projectId = Tools.toBigInteger(job.getProjectID());
                    vo.setDevelopmentLevel(projectDevelopmentLevels.getOrDefault(projectId, ""));
                    return vo;
                })
                .collect(Collectors.toList());

        PageInfo<RTExecutorJobVO> resultPage = new PageInfo<>();
        BeanUtils.copyProperties(pageInfo, resultPage);
        resultPage.setList(executorJobVOList);
        return resultPage;
    }

    private Map<BigInteger, String> fetchProjectDevelopmentLevels(List<BigInteger> projectIds) {
        if (projectIds.isEmpty()) {
            return Collections.emptyMap();
        }
        try {
            HttpHeaders headers = new HttpHeaders();
            headers.set(HttpHeaders.AUTHORIZATION, TyAccountContext.getToken());
            headers.set(HttpHeaders.CONTENT_TYPE, "application/json");
            TyplmQueryIbaDTO queryDTO = new TyplmQueryIbaDTO();
            queryDTO.setIsAdapter(Boolean.TRUE);
            queryDTO.setOtype(TableTypeConstrant.TY_PROJECT_TYPE);
            projectIds.forEach(projectId -> {
                TyplmObjOidAndLogicalDTO oidAndLogical = new TyplmObjOidAndLogicalDTO();
                oidAndLogical.setOid(projectId);
                oidAndLogical.setLogicalIdentifier(TableTypeConstrant.TY_PROJECT_TYPE);
                queryDTO.getOidAndLogicalList().add(oidAndLogical);
            });
            HttpEntity<TyplmQueryIbaDTO> requestEntity = new HttpEntity<>(queryDTO, headers);
            ResponseEntity<ResponseResult<Map<String, Map<String, List<String>>>>> responseEntity = restTemplate.exchange(
                    mainGetIbaServiceUrl,
                    HttpMethod.POST,
                    requestEntity,
                    new ParameterizedTypeReference<ResponseResult<Map<String, Map<String, List<String>>>>>() {}
            );
            Map<BigInteger, String> resultMap = new HashMap<>();
            if (responseEntity.getStatusCode().is2xxSuccessful()) {
                ResponseResult<Map<String, Map<String, List<String>>>> result = responseEntity.getBody();
                if (ObjectUtil.isNotEmpty(result)) {
                    assert result != null;
                    if (result.isSuccess()) {
                        Map<String, Map<String, List<String>>> data = result.getData();
                        if (MapUtil.isNotEmpty(data)) {
                            data.forEach((oidStr, attributes) -> {
                                try {
                                    BigInteger projectId = new BigInteger(oidStr);
                                    if (attributes.containsKey(RTPlmConstant.DEVELOPMENLEVEL)) {
                                        List<String> values = attributes.get(RTPlmConstant.DEVELOPMENLEVEL);
                                        if (!values.isEmpty()) {
                                            resultMap.put(projectId, values.get(0));
                                        }
                                    }
                                } catch (NumberFormatException e) {
                                    log.error("项目ID格式错误: {}", oidStr, e);
                                }
                            });
                        }
                    }
                }
            }
            return resultMap;
        } catch (Exception e) {
            log.error("批量获取项目属性失败", e);
            return Collections.emptyMap();
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updatePreConstraint(String taskOid){
        TyppmJobVO taskVo = getJobByOID(taskOid);
        if(ObjectUtil.isEmpty(taskVo)){
            return;
        }
        String projectID = taskVo.getProjectID();

        BatchInsertDocVO batchInsetDocVO = new BatchInsertDocVO();
        List<DocDetailsVO> docList =new ArrayList<>();
        batchInsetDocVO.setDocOtype("add");
        batchInsetDocVO.setLocalRepositoryUid("javaPDM");
        batchInsetDocVO.setParentNodeOid(taskOid);
        batchInsetDocVO.setParentNodeOtype(TableTypeConstrant.TY_JOB_OTYPE);
        batchInsetDocVO.setProjectOid(projectID);
        batchInsetDocVO.setType("INPUT");

        //获取前置计划OID
        List<BigInteger> preTaskOidList = rtProjectMapper.selectProceedById(new BigInteger(taskVo.getActivityID()));
        if (CollUtil.isEmpty(preTaskOidList)) {
            return;
        }
        //获取前置任务OID
        List<BigInteger> preJobList = rtProjectMapper.selectProceeJobById(preTaskOidList);
        if (CollUtil.isEmpty(preJobList)) {
            return;
        }
        List<DeliverableLinkDO> deliverableLinkDOS = new ArrayList<>();
        for (BigInteger oid : preJobList) {
            List<RemoteObjDTO> remoteObjDTOS = thirdRemoteRequestService.queryDeliverableByParent(String.valueOf(oid), taskVo.getOtype(), "OUT", projectID);
            if (CollUtil.isNotEmpty(remoteObjDTOS)) {
                remoteObjDTOS.forEach(remoteObjDto -> remoteObjDto.setFrontStatus(true));
                remoteObjDTOS.forEach(remoteObjDto -> remoteObjDto.setFrontStatusCN("是"));
                for (RemoteObjDTO remoteObjDTO : remoteObjDTOS) {
                    dealDocVO(remoteObjDTO, docList);
                    String extPros = remoteObjDTO.getExtPros();
                    JSONObject prosData = JSONObject.parseObject(extPros);
                    DeliverableLinkDO deliverableLinkDO = new DeliverableLinkDO();
                    deliverableLinkDO.setJobOid(taskOid);
                    deliverableLinkDO.setLinkMasteredOid(prosData.getString("masteredoid"));
                    deliverableLinkDO.setLinkCode(remoteObjDTO.getCode());
                    deliverableLinkDOS.add(deliverableLinkDO);
                }
            }
        }
        if(CollUtil.isEmpty(docList)){
            return;
        }
        batchInsetDocVO.setDocList(docList);
        this.typpmRemotePdmService.insertRemoteDoc(batchInsetDocVO);
        for (DeliverableLinkDO deliverableLinkDO : deliverableLinkDOS) {
            BigInteger oid = snowflakeIdComponent.getInstance().nextId();
            deliverableLinkDO.setOid(oid);
            deliverableLinkMapper.insert(deliverableLinkDO);
        }
    }

    private void dealDocVO(RemoteObjDTO remoteObjDTO, List<DocDetailsVO> docList) {
        SearchConditionDTO conditionDTO=new SearchConditionDTO();
        conditionDTO.setConditionJoint("and");
        conditionDTO.setKeyword(remoteObjDTO.getCode());
        conditionDTO.setNeedCount(false);
        conditionDTO.setPageNum(1);
        conditionDTO.setPageSize(1000);
        JSONObject params = (JSONObject) JSON.toJSON(conditionDTO);
        try {
            String responseData = commonService.postRequest(queryConditionUrl, params.toJSONString());
            ObjectMapper objectMapper = new CustomObjectMapper();
            ResponseResult<PageInfo<ResultEntityVO>> responseResult = objectMapper.readValue(
                    responseData,
                    new TypeReference<ResponseResult<PageInfo<ResultEntityVO>>>() {}
            );
            if (ObjectUtil.isNotNull(responseResult)) {
                List<ResultEntityVO> remoteObjList = responseResult.getData().getList();
                List<ResultEntityVO> filteredList = remoteObjList.stream()
                        .filter(vo -> Objects.equals(vo.getObjectNumber(), remoteObjDTO.getCode()))
                        .collect(Collectors.toList());
                if (CollUtil.isEmpty(filteredList) || filteredList.size() != 1) {
                    throw RTErrorCodeEnum.TYPEOBJ_NOT_EXIST.getException("该类型对象不存在或编码重复");
                }
                List<DocDetailsVO> docDetailsVOS = BeanUtil.copyToList(filteredList, DocDetailsVO.class);
                docList.add(docDetailsVOS.get(0));
            } else {
                assert responseResult != null;
                throw new RuntimeException("接口返回异常：" + responseResult.getMessage());
            }
        }catch (Exception e){
            log.error("查询条件失败", e);
            e.printStackTrace();
        }
    }

    private TyppmJobVO getJobByOID(String oid) {
        List<TyppmJobDTO> list = this.typpmJobService.getJobByOid(oid);
        List<TyppmJobVO> result = new ArrayList<>();
        if (CollUtil.isNotEmpty(list)) {
            TyppmJobDTO job = list.get(0);
            String activityID = job.getActivityID();
            TyppmJobVO typpmJobVO = new TyppmJobVO();
            Boolean milestone = false;
            Boolean controlled = false;
            if (CharSequenceUtil.isNotBlank(activityID)) {
                TyppmPlanActive planActive = this.planActiveService.selectOneByOID(activityID);
                milestone = planActive.getMilestone();
                controlled = planActive.getControlled();
            }

            this.baseConvert.copyProperties(job, typpmJobVO);
            typpmJobVO.setMilestone(milestone);
            typpmJobVO.setControlled(controlled);
            if (typpmJobVO.getWorkload() == null) {
                typpmJobVO.setWorkload(0.0);
            }

            result.add(typpmJobVO);
        }
        if (!CollectionUtils.isEmpty(result)) {
            Map<String, String> enumValueMap = this.typpmDashboardService.getEnumValueMap("Job State");
            if (!Objects.isNull(enumValueMap)) {
                this.setJobVODisplayName(result, enumValueMap);
            }
        }
        return CollUtil.isNotEmpty( result) ? result.get(0) : null;
    }

    private void setJobVODisplayName(List<TyppmJobVO> list, Map<String, String> enumValueMap) {
        TyppmJobVO job;
        for(Iterator<TyppmJobVO> var3 = list.iterator(); var3.hasNext(); job.setStateDisplayName(enumValueMap.get(
                ProjectMngStateConstant.getJobStateValueMap(job.getState())))) {
            job = var3.next();
            if (!CollectionUtils.isEmpty(job.getChildren())) {
                this.setJobVODisplayName(job.getChildren(), enumValueMap);
            }
        }

    }


//    public void updatePreConstraint(String activeID, int pageNum, int pageSize) {
//        List<RemoteObjDTO> remoteObjDTOS = new ArrayList<>();
//        BigInteger curJobId = rtProjectMapper.selectJobById(Tools.toBigInteger(activeID));
//        if (ObjectUtil.isNull(curJobId)){
//            return;
//        }
//        List<BigInteger> proceeds = rtProjectMapper.selectProceedById(Tools.toBigInteger(activeID));
//        if (CollUtil.isEmpty(proceeds)){
//            return;
//        }
//        List<BigInteger> jobList = rtProjectMapper.selectProceeJobById(proceeds);
//        BigInteger projectOid = rtProjectMapper.selectProjectById(curJobId);
//        //获取前置的交付物
//        if (CollUtil.isEmpty(jobList)) {
//            return ;
//        }
//
//        BatchInsertDocVO batchInsetDocVO = new BatchInsertDocVO();
//        List<DocDetailsVO> docList=new ArrayList<>();
//        batchInsetDocVO.setDocOtype("add");
//        batchInsetDocVO.setLocalRepositoryUid("javaPDM");
//        batchInsetDocVO.setParentNodeOid(curJobId.toString());
//        batchInsetDocVO.setParentNodeOtype(TableTypeConstrant.TY_JOB_OTYPE);
//        batchInsetDocVO.setProjectOid(projectOid.toString());
//        batchInsetDocVO.setType("INPUT");
//
//        for (BigInteger jobId : jobList) {
//            List<RemoteObjDTO> outRemoteObjs = thirdRemoteRequestService.queryDeliverableByParent(
//                    Tools.toString(jobId),
//                    TableTypeConstrant.TY_JOB_OTYPE, RTPlmConstant.OUT, Tools.toString(projectOid));
//            outRemoteObjs.forEach(remoteObjDto -> remoteObjDto.setFrontStatus("是"));
//            remoteObjDTOS.addAll(outRemoteObjs);
//        }
//
//        for (RemoteObjDTO remoteObjDTO : remoteObjDTOS) {
//            SearchConditionDTO conditionDTO=new SearchConditionDTO();
//            conditionDTO.setConditionJoint("and");
//            conditionDTO.setKeyword(remoteObjDTO.getCode());
//            conditionDTO.setNeedCount(false);
//            conditionDTO.setPageNum(1);
//            conditionDTO.setPageSize(1000);
//
//            HttpHeaders headers = new HttpHeaders();
//            headers.set(HttpHeaders.AUTHORIZATION, TyAccountContext.getToken());
//            headers.set(HttpHeaders.CONTENT_TYPE, "application/json");
//            HttpEntity<SearchConditionDTO> requestEntity = new HttpEntity<>(conditionDTO, headers);
//
//            ResponseEntity<String> responseEntity = restTemplate.exchange(
//                    queryConditionUrl,
//                    HttpMethod.POST,
//                    requestEntity,
//                    String.class
//            );
//            if (responseEntity.getStatusCode().is2xxSuccessful()) {
//                String jsonBody = responseEntity.getBody();
//                ObjectMapper objectMapper = new CustomObjectMapper(); // 应用日期解析规则
//                try {
//                    ResponseResult<PageInfo<ResultEntityVO>> responseResult = objectMapper.readValue(
//                            jsonBody,
//                            new TypeReference<ResponseResult<PageInfo<ResultEntityVO>>>() {}
//                    );
//                    if (ObjectUtil.isNotNull(responseResult)) {
//                        // 返回分页数据
//                        List<ResultEntityVO> remoteObjList = responseResult.getData().getList();
//                        List<ResultEntityVO> filteredList = remoteObjList.stream()
//                                .filter(vo -> Objects.equals(vo.getObjectNumber(), remoteObjDTO.getCode()))
//                                .collect(Collectors.toList());
//                        if (CollUtil.isEmpty(filteredList) || filteredList.size() != 1) {
//                            throw RTErrorCodeEnum.TYPEOBJ_NOT_EXIST.getException("该类型对象不存在或编码重复");
//                        }
//                        List<DocDetailsVO> docDetailsVOS = BeanUtil.copyToList(filteredList, DocDetailsVO.class);
//                        docList.addAll(docDetailsVOS);
//                    } else {
//                        assert responseResult != null;
//                        throw new RuntimeException("接口返回异常：" + responseResult.getMessage());
//                    }
//                } catch (JsonProcessingException e) {
//                    throw new RuntimeException(e);
//                }
//            }
//        }
//
//        batchInsetDocVO.setDocList(docList);
//        this.typpmRemotePdmService.insertRemoteDoc(batchInsetDocVO);
//    }

    @Override
    public String importAnnualProject(MultipartFile file) throws Exception {
        if (file.isEmpty()) {
            throw SystemErrorCodeEnum.EXCEL_ISNULL.getException("上传的文件为空");
        }
        if (!isExcelFile(file)) {
            throw new UtilsException("只支持 Excel 文件 (.xlsx, .xls)");
        }
        List<Map<String, String>> data = ExcelParser.parseExcel(file);
        List<String> l = data.stream().flatMap(map -> map.keySet().stream()).collect(Collectors.toList());
        if (!l.contains("项目开发令") || !l.contains("项目名称") || !l.contains("推进方式") || !l.contains("等级")) {
            throw new RuntimeException("请根据年度计划模板上传文件！！");
        }
        List<Map<String, String>> projectDataList = data.stream()
                .filter(map -> map.values().stream().anyMatch(Objects::nonNull))
                .collect(Collectors.toList());
        // 1. 解析表头和里程碑（一次性处理）
        Map<String, String> interNameMap = new HashMap<>();
        List<String> milestoneList = new ArrayList<>();
        parseHeaderAndMilestone(projectDataList, interNameMap, milestoneList);
        // 2.批量处理项目名称校验与删除（复用并行流）
        List<Map<String, String>> duplicateProjectMaps = new ArrayList<>();
        try {

            duplicateProjectMaps = handleProjectDelete(projectDataList, milestoneList);
        } catch (Exception e) {

        }
        // 定义集合存储所有错误信息
        List<String> errorMessages = new ArrayList<>();
        List<String> Messages = new ArrayList<>();
        Map<String, String> OrderMap = new HashMap<>();
        Map<String, String> order = new HashMap<>();
        List same = new ArrayList<>();
        List exist = new ArrayList<>();
        int count = projectDataList.size() - 1 + same.size() + exist.size();
        //去重
        for (int i = 0; i < projectDataList.size(); i++) {
            Map<String, String> row = projectDataList.get(i);
            String projectCode = row.get("项目开发令");
            if (!StrUtil.isEmpty(order.get(projectCode))) {
                same.add(i);
            }

            if (row.containsKey("项目开发令")) {
                order.put(row.get("项目开发令"), "项目开发令");
            }
        }
        for (int i = same.size() - 1; i >= 0; i--) {
            Integer o = (Integer) same.get(i);
            int ob = o;
            projectDataList.remove(ob);
        }
        //批量更新项目
        for (int j = 0; j < projectDataList.size(); j++) {
            Map<String, String> map = projectDataList.get(j);
            if (map.containsKey("项目开发令")) {
                String projectCode = map.get("项目开发令");
                String pjname = rtProjectMapper.selectProjectNameByDevCode(projectCode);
                List<BigInteger> pjOid = rtProjectMapper.selectProjectOidByName(pjname);
                if (!CollUtil.isEmpty(pjOid)) {
                    List<BigInteger> jobs = rtProjectMapper.selectJobByOID(pjOid.get(0).toString());
                    boolean isSecond = false;
                    for (BigInteger bigInteger : jobs) {
                        BigInteger parentJob = rtProjectMapper.selectParentJob(bigInteger);
                        if (parentJob != null) {
                            isSecond = true;
                        }
                    }
                    if (isSecond) {
                        errorMessages.add("项目开发令为：" + projectCode + "的" + pjname + "项目有二级任务，请检查");
                    }
                }else  {
                    continue;
                }

                if (!errorMessages.isEmpty()) {
                    // 拼接所有错误信息（用换行分隔）
                    String errorMsg = String.join("；", errorMessages);
                    throw new Exception(errorMsg);
                }
                List<BigInteger> bigIntegerList = rtProjectMapper.selectAttrByDisplayName("项目开发令");
                List<String> list = rtProjectMapper.selectOrderIsExist(bigIntegerList.get(0).toString(),
                                                                       projectCode);
                if (CollUtil.isNotEmpty(list)) {
                    exist.add(j);
                    UpdateProjectOwnerDTO updateProjectOwnerDTO = new UpdateProjectOwnerDTO();
                    List<BigInteger> bigIntegers = rtProjectMapper.selectProjectIdByCode(projectCode);
                    updateProjectOwnerDTO.setProjectId(bigIntegers.get(0).toString());
                    //更新时间
                    if (map.containsKey("项目开始时间")) {
                        String projectStartTime = map.get("项目开始时间");
                        // 定义日期格式：年/月/日（支持单/双位数的月和日）
                        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
                        // 转换为 LocalDate
                        LocalDate excelDate = LocalDate.parse(projectStartTime, formatter);
                        LocalDateTime localDateTime = rtProjectMapper.selectStartDateByOid(bigIntegers.get(0));
                        LocalDate localDate = localDateTime.toLocalDate();
                        updateActualDate(localDate, excelDate, bigIntegers.get(0));
                    }

                    //更换项目经理
                    Map<String, String> stringStringMap = rtProjectMapper.selectDevelopmentOrderByOrderName(
                            projectCode);
                    if (CollUtil.isNotEmpty(stringStringMap)) {
                        String responsiblePerson = stringStringMap.get("RESPONSIBLE_PERSON");
                        String projectName = stringStringMap.get("PROJECT_NAME");
                        List<BigInteger> groupOid = rtProjectMapper.selectGroupByName("PROJECT DIRECTOR",
                                                                                      bigIntegers.get(0));
                        if (CollUtil.isNotEmpty(groupOid)) {
                            List<BigInteger> userIds = rtProjectMapper.selectUserIdByGroupId(groupOid.get(0));
                            UserDO userDO = rtProjectMapper.selectUserByName(responsiblePerson);
                            boolean flag = false;
                            for (BigInteger userId : userIds) {
                                if (!userId.equals(userDO.getOid())) {
                                    Map<String, Object> pjMap = new HashMap<>();
                                    pjMap.put("projectOid", updateProjectOwnerDTO.getProjectId());
                                    pjMap.put("userOid", userId.toString());
                                    pjMap.put("roleName", "PROJECT DIRECTOR");
                                    typpmProjectService.deleteProjectManagerOrAdministrator(pjMap);
                                } else {
                                    flag = true;
                                }
                            }
                            if (!flag) {
                                Map<String, Object> pmMap = new HashMap<>();
                                pmMap.put("projectOid", updateProjectOwnerDTO.getProjectId());
                                pmMap.put("roleName", "PROJECT DIRECTOR");
                                pmMap.put("userOid", userDO.getOid());
                                typpmProjectService.doInsertProjectManagerOrAdministrator(pmMap);
                            }
                        }
                    }
                    String designer = map.get("主设计师工号");
                    UserDO userByNumber = rtProjectMapper.selectUserByNumber(designer);
                    TyppmTeamTreeDTO typpmTeamTreeDTO = new TyppmTeamTreeDTO();
                    typpmTeamTreeDTO.setOtype("ty.inteplm.org.CTyUser");
                    typpmTeamTreeDTO.setOid(userByNumber.getOid().toString());
                    typpmTeamTreeDTO.setName(userByNumber.getName());
                    typpmTeamTreeDTO.setFullName(userByNumber.getFullname());
                    typpmTeamTreeDTO.setUserFullName(userByNumber.getFullname());
                    String linkId = snowflakeIdComponent.getInstance().nextId().toString();
                    typpmTeamTreeDTO.setLinkID(linkId);
                    addMainDesigner(pjOid.get(0).toString(), typpmTeamTreeDTO);

                    //增加任务
                    //生成项目计划
                    List<PlusTaskSaveDTO> plusTaskSaveDTOS = new ArrayList<>();
                    Map<String, String> Map = extractColumnsBySuffix(map, "结束时间");
                    TyppmProjectDTO typpmProjectDTO = new TyppmProjectDTO();
                    typpmProjectDTO.setOid(pjOid.get(0).toString());
                    TyppmPlan plan = new TyppmPlan();
                    String projectOid = pjOid.get(0).toString();
                    BigInteger planOid = rtProjectMapper.selectPlanOIDByProjectOID(projectOid);
                    plan.setOid(planOid.toString());
                    handlePlanTasks(map, Map, plan, typpmProjectDTO, milestoneList, plusTaskSaveDTOS);
                    BatchLockParams batchLockParams = new BatchLockParams();
                    PlanActiveLockDTO planActiveLockDTO = new PlanActiveLockDTO();
                    planActiveLockDTO.setOid(planOid.toString());
                    planActiveLockDTO.setOtype("ty.projectmng.admin.CTyPlan");
                    batchLockParams.setPlan(planActiveLockDTO);
                    //planActiveService.lockAllPlanActive(batchLockParams);
                    this.ganttChartService.saveTableTreeEx(plan.getOid(), plusTaskSaveDTOS);
                }
            }
        }
        for (int i = exist.size() - 1; i >= 0; i--) {
            Integer o = (Integer) exist.get(i);
            int ob = o;
            projectDataList.remove(ob);
        }
        for (int j = 1; j < projectDataList.size(); j++) {
            Map<String, String> map = projectDataList.get(j);
            if (map.containsKey("项目开发令")) {
                String projectCode = map.get("项目开发令");
                Map<String, String> stringStringMap = rtProjectMapper.selectDevelopmentOrderByOrderName(projectCode);
                if (CollUtil.isEmpty(stringStringMap)) {
                    errorMessages.add("第" + j + "行开发令不存在（开发令：" + projectCode + "）");
                }
                List<BigInteger> bigIntegerList = rtProjectMapper.selectAttrByDisplayName("项目开发令");
                List<String> list = rtProjectMapper.selectOrderIsExist(bigIntegerList.get(0).toString(),
                                                                       projectCode);
                if (CollUtil.isNotEmpty(list)) {

                }
                OrderMap.put(projectCode, map.get("项目名称"));
            }

            // 记录当前行索引（如果需要定位出行号，可添加索引变量）
            // int rowIndex = projectDataList.indexOf(map) + 1; // 从1开始计数

            if (map.containsKey("项目经理工号")) {
                String manager = map.get("项目经理工号");
                if (ObjectUtil.isEmpty(manager)) {
                    // 添加错误信息（可包含行标识，如“第X行：项目经理工号为空”）
                    errorMessages.add("第" + j + "行，项目经理工号为空");
                } else {
                    UserDO userByNumber = rtProjectMapper.selectUserByNumber(manager);
                    if (ObjectUtil.isEmpty(userByNumber)) {
                        errorMessages.add("第" + j + "行项目经理工号不存在（工号：" + manager + "）");
                    }
                    String order1 = map.get("项目开发令");
                    Map<String, String> stringStringMap = rtProjectMapper.selectDevelopmentOrderByOrderName(order1);
                    if (CollUtil.isNotEmpty(stringStringMap)) {
                        if (!userByNumber.getFullname().equals(stringStringMap.get("RESPONSIBLE_PERSON"))) {
                            errorMessages.add(
                                    "第" + j + "行项目经理工号与开发令负责人不一致（工号：" + manager + "）");
                        }
                    }

                }
            }

            if (map.containsKey("主设计师工号")) {
                String designer = map.get("主设计师工号");
                if (ObjectUtil.isEmpty(designer)) {
                    errorMessages.add("第" + j + "行主设计师工号为空");
                } else {
                    UserDO userByNumber = rtProjectMapper.selectUserByNumber(designer);

                    if (ObjectUtil.isEmpty(userByNumber)) {
                        errorMessages.add("第" + j + "行主设计师工号不存在（工号：" + designer + "）");
                    }
                }
            }
        }
        // 循环结束后，若有错误则统一抛出
        if (!errorMessages.isEmpty()) {
            // 拼接所有错误信息（用换行分隔）
            String errorMsg = String.join("；", errorMessages);
            throw new Exception(errorMsg);
        }
        Set<String> duplicateProjectNames = duplicateProjectMaps.stream()
                .map(map -> map.get("项目名称")) // 提取要剔除的项目名称
                .filter(Objects::nonNull)       // 排除项目名为null的无效值
                .collect(Collectors.toSet());   // 转成Set，方便后续快速判断

        List<Map<String, String>> remainingProjects = projectDataList.stream()
                .filter(map -> map.values().stream().anyMatch(Objects::nonNull))
                .filter(map -> {
                    String projectName = map.get("项目名称");
                    return projectName != null && !duplicateProjectNames.contains(projectName);
                })
                .collect(Collectors.toList());
        if (ObjectUtil.isEmpty(remainingProjects)) {
            return "实际修改" + exist.size() + "条项目计划";
        }
        log.info("============年度计划项目开始导入===========>>,{}", remainingProjects.size());
        for (Map<String, String> row : remainingProjects) {
            //获取新的活动计划
            Map<String, String> newPlansMap = extractColumnsBySuffix(row, "结束时间");
            //名称标准化校验
            List<String> names = rtTaskMapper.selectStandardName();
            if (CollUtil.isNotEmpty(names)) {
                List<String> errNames = newPlansMap.keySet().stream().filter(f -> !names.contains(f)).collect(
                        Collectors.toList());
                if (CollUtil.isNotEmpty(errNames)) {
                    throw new RuntimeException("不符合标准名称的项目计划：" + JSONObject.toJSONString(errNames));
                }
            }
            //获取内部名称字段的映射关系
            if (StringUtils.equals(row.get("序号"), "内部名称")) {
                continue;
            }
            List<Map<String, String>> maps = rtProjectMapper.selectDevelopmentOrder(
                    Arrays.asList(row.get("项目开发令")));
            if (CollUtil.isNotEmpty(maps)) {
                row.put("项目经理", maps.get(0).get("RESPONSIBLE_PERSON"));
            }

            ProjectVO projectVO = buildProjectVO(row, interNameMap);
            if (Boolean.TRUE.equals(
                    com.hustcad.plm.rpm.util.StringUtils.checkNameFormat(projectVO.getName(), this.notSpecialChar))) {
                throw com.hustcad.plm.pdm.core.handle.exception.SystemErrorCodeEnum.REGX_ACTIVE_NAME_SPECIAL.message(
                        new Object[]{projectVO.getName(), this.notSpecialChar});
            } else {

                // 生成项目
                TyppmProjectDTO map = BeanMapper.map(projectVO, TyppmProjectDTO.class);
                map.setTypeOID(projectVO.getTypeOid());
                map.setTypeOTYPE(projectVO.getTypeOtype());
                map.setPercentWorkComplete((double) 0);
                map.setTypeLogicalIdentifier(projectVO.getTypeLogicalIdentifier());
                map.setTypeName(projectVO.getTypeName());
                map.getIbaAttribute().put("encryptionlevel", Arrays.asList("非密"));
                map.getIbaAttribute().put("DevelopmentOverview", Arrays.asList(row.get("开发概要")));
                map.getIbaAttribute().put("ProjectSection", Arrays.asList(row.get("板块")));
                if (CollUtil.isNotEmpty(maps)) {
                    Map<String, String> stringStringMap = maps.get(0);
                    //项目名称
                    map.setName(stringStringMap.get("PROJECT_NAME"));
                    //开发令号
                    if (StrUtil.isNotBlank(stringStringMap.get("DEV_ORDER_NUMBER"))) {
                        map.getIbaAttribute().put("DevelopmentOrder",
                                                  Arrays.asList(stringStringMap.get("DEV_ORDER_NUMBER")));
                    }
                    //机型号
                    if (StrUtil.isNotBlank(stringStringMap.get("AIRCRAFT_MODEL"))) {
                        map.getIbaAttribute().put("productType", Arrays.asList(stringStringMap.get("AIRCRAFT_MODEL")));
                    }
                    //项目开发令
                    if (StrUtil.isNotBlank(stringStringMap.get("PROJECT_CODE"))) {
                        map.getIbaAttribute().put("ProjectDevelopmentOrder",
                                                  Arrays.asList(stringStringMap.get("PROJECT_CODE")));
                    }
                    //开发分级
                    if (StrUtil.isNotBlank(stringStringMap.get("DEV_CATEGORY"))) {
                        map.getIbaAttribute().put("DevelopmentLevel",
                                                  Arrays.asList(stringStringMap.get("DEV_CATEGORY")));
                    }
                    //开发类型
                    if (StrUtil.isNotBlank(stringStringMap.get("SECTOR"))) {
                        map.getIbaAttribute().put("DevelopmentType",
                                                  Arrays.asList(stringStringMap.get("DEVELOPMENT_TYPE")));
                    }
                    //开发等级
                    if (StrUtil.isNotBlank(stringStringMap.get("SECTOR"))) {
                        map.getIbaAttribute().put("Grade", Arrays.asList(stringStringMap.get("DEV_LEVEL")));
                    }
                    //项目版块
                    if (StrUtil.isNotBlank(stringStringMap.get("SECTOR"))) {
                        map.getIbaAttribute().put("ProjectSection", Arrays.asList(stringStringMap.get("SECTOR")));
                    }
                    //产品系列
                    if (StrUtil.isNotBlank(stringStringMap.get("PROJECT_SERIES"))) {
                        map.getIbaAttribute().put("ProductSeries",
                                                  Arrays.asList(stringStringMap.get("PROJECT_SERIES")));
                    }
                    //产品类别
                    if (StrUtil.isNotBlank(stringStringMap.get("PROJECT_CATEGORY"))) {
                        map.getIbaAttribute().put("ProductCategory",
                                                  Arrays.asList(stringStringMap.get("PROJECT_CATEGORY")));
                    }
                    //系数
                    if (StrUtil.isNotBlank(stringStringMap.get("COEFFICIENT"))) {
                        map.getIbaAttribute().put("Coefficient", Arrays.asList(stringStringMap.get("COEFFICIENT")));
                    }

                    if (StrUtil.isNotBlank(row.get("大类/客户"))) {
                        map.getIbaAttribute().put("MainCategory/Client", Arrays.asList(row.get("大类/客户")));
                    }
                    if (StrUtil.isNotBlank(stringStringMap.get("PROJECT_SERIES"))) {
                        map.getIbaAttribute().put("ProjectScction",
                                                  Arrays.asList(stringStringMap.get("PROJECT_SERIES")));
                    } else {
                        map.getIbaAttribute().put("ProjectScction", Arrays.asList(row.get("板块")));
                    }
                    if (StrUtil.isNotBlank(row.get("变更备注"))) {
                        map.getIbaAttribute().put("ChangeRemarks", Arrays.asList(row.get("变更备注")));
                    }
                    //项目经理
                    String person = stringStringMap.get("RESPONSIBLE_PERSON");
                    if (StrUtil.isNotBlank(person)) {
                        row.put("项目经理", person);
                    }
                    //推进方式
                    map.getIbaAttribute().put("PushMode", Arrays.asList(stringStringMap.get("BOOST")));
                } else {
                    throw new RuntimeException("开发令:" + projectVO.getProjectNumber() + "不存在");
                }
                map.getIbaAttribute().put("IsYearImport", Arrays.asList("Yes"));
                TyppmProjectDTO typpmProjectDTO = this.projectServiceForPdm.insertProject(map);
                TyppmPlan plan = typpmProjectDTO.getPlan();

                //生成项目计划
                List<PlusTaskSaveDTO> plusTaskSaveDTOS = new ArrayList<>();
                Map<String, String> Map = extractColumnsBySuffix(row, "结束时间");
                handlePlanTasks(row, Map, plan, typpmProjectDTO, milestoneList, plusTaskSaveDTOS);
                BatchLockParams batchLockParams = new BatchLockParams();
                PlanActiveLockDTO planActiveLockDTO = new PlanActiveLockDTO();
                planActiveLockDTO.setOid(plan.getOid());
                planActiveLockDTO.setOtype("ty.projectmng.admin.CTyPlan");
                batchLockParams.setPlan(planActiveLockDTO);
                //planActiveService.lockAllPlanActive(batchLockParams);
                this.ganttChartService.saveTableTreeEx(plan.getOid(), plusTaskSaveDTOS);
                String designer = row.get("主设计师工号");
                UserDO userByNumber = rtProjectMapper.selectUserByNumber(designer);
                TyppmTeamTreeDTO typpmTeamTreeDTO = new TyppmTeamTreeDTO();
                typpmTeamTreeDTO.setOtype("ty.inteplm.org.CTyUser");
                typpmTeamTreeDTO.setOid(userByNumber.getOid().toString());
                typpmTeamTreeDTO.setName(userByNumber.getName());
                typpmTeamTreeDTO.setFullName(userByNumber.getFullname());
                typpmTeamTreeDTO.setUserFullName(userByNumber.getFullname());
                String linkId = snowflakeIdComponent.getInstance().nextId().toString();
                typpmTeamTreeDTO.setLinkID(linkId);
                addMainDesigner(typpmProjectDTO.getOid(), typpmTeamTreeDTO);
            }
        }


        Messages.add("应导入" + count + "条项目计划");
        Messages.add("实际修改" + exist.size() + "条项目计划");
        Messages.add("实际导入" + OrderMap.size() + "条项目计划");

        // 拼接所有信息（用换行分隔）
        String Msg = String.join("；", Messages);
        return Msg;


    }

    private void handlePlanTasks(Map<String, String> row, Map<String, String> map, TyppmPlan plan,
                                 TyppmProjectDTO typpmProjectDTO,
                                 List<String> milestoneList, List<PlusTaskSaveDTO> plusTaskSaveDTOS) {
        String lastEndDateSrt = row.get("项目开始时间");

        LocalDate projectStartDate = LocalDate.parse(lastEndDateSrt, RTPlmConstant.DATE_FORMATTER);
        projectStartDate = adjustToNextWorkday(projectStartDate); // 关键调整
        LocalDateTime projectStartDateTime = projectStartDate.atStartOfDay(); // 00:00:00
        String firstPlanStartTime = RTPlmConstant.DATETIME_FORMATTER.format(projectStartDateTime);

        boolean isFirstValidPlan = true;
        String previousEndTime = null;
        int planSequence = 1;

        for (Map.Entry<String, String> planEntry : map.entrySet()) {
            String endDateStr = planEntry.getValue(); // 当前计划的结束时间
            if (StringUtils.isEmpty(endDateStr)) {
                continue;
            }
            // 处理当前计划的结束时间（转为 23:59:59 格式）
            LocalDate endDate = LocalDate.parse(endDateStr, RTPlmConstant.DATE_FORMATTER);
            endDate = adjustToNextWorkday(endDate); // 关键调整
            LocalDateTime endDateTime = endDate.atTime(23, 59, 59); // 23:59:59
            String currentEndTime = RTPlmConstant.DATETIME_FORMATTER.format(endDateTime);

            // 创建计划DTO
            PlusTaskSaveDTO plusTaskSaveDTO = new PlusTaskSaveDTO();
            plusTaskSaveDTO.setActive("1");
            plusTaskSaveDTO.setConstraintType("0");
            plusTaskSaveDTO.setCritical("0");
            plusTaskSaveDTO.setOutLineLevel("1");
            plusTaskSaveDTO.setOutLineNumber(String.valueOf(planSequence++));
            plusTaskSaveDTO.setLineNumber(plusTaskSaveDTO.getOutLineNumber());// 设置序号
            plusTaskSaveDTO.setName(planEntry.getKey()); // 设置计划名称
            plusTaskSaveDTO.setEarlyFinish(currentEndTime); // 设置结束时间
            plusTaskSaveDTO.setFinishDate(currentEndTime); // 设置结束时间
            plusTaskSaveDTO.setWork("0");
            plusTaskSaveDTO.setPercentWorkComplete("0");

            // 设置开始时间
            String currentStartTime;
            if (isFirstValidPlan) {
                // 第一个计划：使用项目开始时间（00:00:00）
                currentStartTime = firstPlanStartTime;
                isFirstValidPlan = false;
            } else {
                // 后续计划：使用前一个计划结束时间的下一秒（00:00:00）
                LocalDateTime nextStartTime = LocalDateTime.parse(previousEndTime,
                                                                  RTPlmConstant.DATETIME_FORMATTER)
                        .plusSeconds(1);
                LocalDate nextStartDate = nextStartTime.toLocalDate();
                // 若开始日期是周末，调整到下周一的00:00:00
                if (isWeekend(nextStartDate)) {
                    nextStartDate = adjustToNextWorkday(nextStartDate);
                    nextStartTime = nextStartDate.atStartOfDay();
                }
                currentStartTime = RTPlmConstant.DATETIME_FORMATTER.format(nextStartTime);
                plusTaskSaveDTO.setConstraintType("4");
            }

            plusTaskSaveDTO.setEarlyStart(currentStartTime);
            plusTaskSaveDTO.setConstraintDate(currentStartTime);
            plusTaskSaveDTO.setStartDate(currentStartTime);

            // 计算工期（毫秒）并设置
            long durationMillis = WorkdayCalculator.calculateWorkdayDuration(
                    currentStartTime,
                    currentEndTime
            );
            plusTaskSaveDTO.setDuration(String.valueOf(durationMillis));

            plusTaskSaveDTO.setOid(snowflakeIdComponent.getInstance().nextId().toString());
            plusTaskSaveDTO.setOtype( TableTypeConstrant.TY_PLAN_ACTIVITY_OTYPE);
            plusTaskSaveDTO.setParentOID(plan.getOid());
            plusTaskSaveDTO.setParentOTYPE(RTPlmConstant.PLAN_TYPE);
            //plusTaskSaveDTO.setRiskType("UNAVAILABLE");
            plusTaskSaveDTO.setJobOID(snowflakeIdComponent.getInstance().nextId().toString());
            plusTaskSaveDTO.setHealthStatusType("UNAVAILABLE");
            plusTaskSaveDTO.setControlled("0");
            plusTaskSaveDTO.setMarkForDelete("0");
            plusTaskSaveDTO.setEffortDriven("0");
            plusTaskSaveDTO.setOwnerOID(TyAccountContext.getUser().getOid().toString());
            //设置项目执行人
            UserDO userDO = rtProjectMapper.selectUserByName(row.get("项目经理"));
            plusTaskSaveDTO.setExecutorOID(userDO.getOid().toString());

            String groupId = rtProjectMapper.selectProjectDirectorGroupId(typpmProjectDTO.getOid());
            BigInteger ownerGroupOid = rtProjectMapper.selectGroupById(typpmProjectDTO.getOid());
            plusTaskSaveDTO.setOwnerGroupOid(ownerGroupOid.toString());
            plusTaskSaveDTO.setExecutorGroupOid(groupId);
            String lateFinish = formatLocalDateTime(LocalDateTime.parse(
                    Objects.requireNonNull(ExcelParser.getProjectEndDateWithTime(row, "结束时间")),
                    WorkdayCalculator.DATE_TIME_FORMATTER));
            plusTaskSaveDTO.setLateStart(formatLocalDateTime(WorkdayCalculator.calculateLateStartFromMillis(LocalDateTime.parse(lateFinish,WorkdayCalculator.DATE_TIME_FORMATTER),durationMillis)));
            plusTaskSaveDTO.setLateFinish(lateFinish);
            plusTaskSaveDTO.setTypeOID("264465053392879616");
            plusTaskSaveDTO.setTypeOTYPE(TableTypeConstrant.TY_TYPE_OTYPE);
            if (milestoneList.contains(planEntry.getKey())) {
                plusTaskSaveDTO.setMilestone("1");
            } else {
                plusTaskSaveDTO.setMilestone("0");
            }
            // 记录当前计划的结束时间，供下一个计划使用
            previousEndTime = currentEndTime;
            plusTaskSaveDTOS.add(plusTaskSaveDTO);
        }
    }

    private  ProjectVO buildProjectVO(Map<String, String> row, Map<String, String> interNameMap) {
        ProjectVO projectVO = new ProjectVO();
        Map<String, List<String>> ibaMap = new HashMap<>();
        projectVO.setPhase("启动");
        BigInteger typeOID= rtProjectMapper.selectProjectTypeByName(row.get("板块"));
        if(ObjectUtil.isNull(typeOID)){
            throw RTErrorCodeEnum.PROJECT_TYPE_NOT_EXIST.getException("项目类型不存在");
        }
        projectVO.setCompletionStatus(0);
        projectVO.setTypeOid(typeOID.toString());
        projectVO.setTypeOtype(TableTypeConstrant.TY_TYPE_OTYPE);
        projectVO.setCategory(row.get("板块"));
        projectVO.setTypeLogicalIdentifier(row.get("板块"));


        projectVO.setName(row.get("项目名称"));
        projectVO.setDescription(row.get("开发概要"));
        projectVO.setProjectNumber(row.get("项目开发令"));

        projectVO.setAdminUserDescription(TyAccountContext.getUser().getDescription());
        projectVO.setAdminUserName(TyAccountContext.getUser().getName());
        projectVO.setAdminUserOid(TyAccountContext.getUser().getOid().toString());
        projectVO.setAdminUserOtype(TyAccountContext.getUser().getOtype());
        projectVO.setOwnerDescription(TyAccountContext.getUser().getDescription());
        //            projectVO.setOwnerName(TyAccountContext.getUser().getName());
        //            projectVO.setOwnerOID(TyAccountContext.getUser().getOid().toString());
        for (Map.Entry<String, String> iba : interNameMap.entrySet()) {
            ibaMap.put(iba.getValue(), Collections.singletonList(row.get(iba.getKey())));
        }
        ibaMap.put("CreationMethod", Collections.singletonList("CreatedAnnually"));
        UserDO userDO = rtProjectMapper.selectUserByName(row.get("项目经理"));
        UserDO userDO1 = rtProjectMapper.selectUserByName(row.get("主设计师"));
        if(ObjectUtil.isNull(userDO) || ObjectUtil.isNull(userDO1)){
            throw RTErrorCodeEnum.USER_NOT_EXIST.getException("用户不存在");
        }
        List<TyppmUser> projectManagerList = Stream.of(userDO, userDO1) // 过滤null
                .map(user -> {
                    TyppmUser typpmUser = new TyppmUser();
                    typpmUser.setOid(user.getOid().toString());
                    typpmUser.setName(user.getName());
                    typpmUser.setDescription(user.getDescription());
                    typpmUser.setOtype(user.getOtype());
                    return typpmUser;
                })
                .collect(Collectors.collectingAndThen(
                        Collectors.toMap(
                                TyppmUser::getOid,  // key: oid
                                Function.identity(), // value: 对象本身
                                (existing, replacement) -> existing
                        ),
                        map -> new ArrayList<>(map.values())
                ));
        projectVO.setProjectManagerList(projectManagerList);
        projectVO.setIbaAttribute(ibaMap);
        projectVO.setStartDate(
                LocalDate.parse(row.get("项目开始时间"), DateTimeFormatter.ISO_LOCAL_DATE).atStartOfDay());

        LocalDateTime finishDate = LocalDateTime.parse(
                Objects.requireNonNull(ExcelParser.getProjectEndDateWithTime(row, "结束时间")),
                WorkdayCalculator.DATE_TIME_FORMATTER);

        projectVO.setFinishDate(finishDate);
        return projectVO;
    }


    private  ProjectVO buildProjectsVO(Map<String, String> row, Map<String, String> interNameMap) {
        ProjectVO projectVO = new ProjectVO();
        Map<String, List<String>> ibaMap = new HashMap<>();
        BeanUtils.copyProperties(row, projectVO);
        projectVO.setPhase("启动");
        String otype = row.get("OTYPE");
        if(ObjectUtil.isNull(otype)){
            throw RTErrorCodeEnum.PROJECT_TYPE_NOT_EXIST.getException("项目类型不存在");
        }
        //通过开发令查取iba属性
        String developmentOrder = row.get("developmentOrder");
        Map<String,String> developmentOrderList=rtProjectMapper.selectDevelopmentOrderByOrderName(developmentOrder);
        Map<String, List<String>> convertedMap = new HashMap<>();

        for (Map.Entry<String, String> entry : developmentOrderList.entrySet()) {
            if(entry.getValue() != null&&entry.getValue()!="") {
                String key = entry.getKey();
                String value = safeToString(entry.getValue());

                // 为每个值创建一个单元素List
                List<String> valueList = new ArrayList<>();
                valueList.add(value);

                // 放入新Map
                convertedMap.put(key, valueList);
            }
        }
        projectVO.setIbaAttribute(convertedMap);
        projectVO.setCompletionStatus(0);
        projectVO.setTypeOid(row.get("TYPEOID"));
        projectVO.setTypeOtype(TableTypeConstrant.TY_TYPE_OTYPE);
        projectVO.setCategory(row.get("CATEGORY"));
        projectVO.setTypeLogicalIdentifier(row.get("板块"));


        projectVO.setName(row.get("NAME"));
        projectVO.setDescription(row.get("DESCRIPTIONCONTAINERINFO"));
        projectVO.setProjectNumber(interNameMap.get("DEV_ORDER_NUMBER"));

        projectVO.setAdminUserDescription(TyAccountContext.getUser().getDescription());
        projectVO.setAdminUserName(TyAccountContext.getUser().getName());
        projectVO.setAdminUserOid(TyAccountContext.getUser().getOid().toString());
        projectVO.setAdminUserOtype(TyAccountContext.getUser().getOtype());
        projectVO.setOwnerDescription(TyAccountContext.getUser().getDescription());
        //            projectVO.setOwnerName(TyAccountContext.getUser().getName());
        //            projectVO.setOwnerOID(TyAccountContext.getUser().getOid().toString());

        for (Map.Entry<String, String> iba : interNameMap.entrySet()) {
            ibaMap.put(iba.getKey(), Collections.singletonList(safeToString(iba.getValue())));
        }
        ibaMap.put("CreationMethod", Collections.singletonList("Reference"));
//        UserDO userDO = rtProjectMapper.selectUserByName(row.get("项目经理"));
//        UserDO userDO1 = rtProjectMapper.selectUserByName(row.get("主设计师"));
        UserDO userDO = rtProjectMapper.selectUserByName(interNameMap.get("RESPONSIBLE_PERSON"));
        if(ObjectUtil.isNull(userDO)){
            throw RTErrorCodeEnum.USER_NOT_EXIST.getException("用户不存在");
        }
        List<TyppmUser> projectManagerList = Stream.of(userDO) // 过滤null
                .map(user -> {
                    TyppmUser typpmUser = new TyppmUser();
                    typpmUser.setOid(user.getOid().toString());
                    typpmUser.setName(user.getName());
                    typpmUser.setDescription(user.getDescription());
                    typpmUser.setOtype(user.getOtype());
                    return typpmUser;
                })
                .collect(Collectors.collectingAndThen(
                        Collectors.toMap(
                                TyppmUser::getOid,  // key: oid
                                Function.identity(), // value: 对象本身
                                (existing, replacement) -> existing
                        ),
                        map -> new ArrayList<>(map.values())
                ));
        projectVO.setProjectManagerList(projectManagerList);
        projectVO.setIbaAttribute(ibaMap);
        String oid = safeToString(row.get("OID"));
        BigInteger bigIntegerOid = new BigInteger(oid);
        LocalDateTime startDate=rtProjectMapper.selectStartDateByOid(bigIntegerOid);
        LocalDateTime endDate=rtProjectMapper.selectEndDateByOid(bigIntegerOid);

        projectVO.setStartDate(startDate);
        projectVO.setFinishDate(endDate);
        return projectVO;
    }

    private void parseHeaderAndMilestone(List<Map<String, String>> dataList,
                                         Map<String, String> interNameMap,
                                         List<String> milestoneList) {
        for (Map<String, String> row : dataList) {
            if ("内部名称".equals(row.get("序号"))) {
                // 解析内部名称映射
                row.forEach((header, internalName) -> {
                    if (internalName != null && internalName.matches("[a-zA-Z]+")) {
                        interNameMap.put(header, internalName);
                    }
                });
                // 解析里程碑
                milestoneList.addAll(row.entrySet().stream()
                                             .filter(entry -> entry.getValue() != null && entry.getKey() != null)
                                             .filter(entry -> entry.getKey().endsWith("结束时间"))
                                             .map(entry -> entry.getKey().substring(0, entry.getKey().length() - 4))
                                             .collect(Collectors.toList()));
                break; // 只处理一次表头
            }
        }
    }

    private  List<Map<String, String>> handleProjectDelete(List<Map<String, String>> projectDataList,List<String> milestoneList)
            throws SQLException {
        for (Map<String, String> map:projectDataList){
            String n = map.get("项目开发令");
            if (StrUtil.isNotBlank(n) && !(n.equals("ProjectDevelopmentOrder"))){
                List<Map<String, String>> maps = rtProjectMapper.selectDevelopmentOrder(Arrays.asList(n));
                if (CollUtil.isNotEmpty(maps)){
                    Map<String, String> stringStringMap = maps.get(0);
                    map.put("项目名称",stringStringMap.get("PROJECT_NAME"));
                }else {
                    throw new RuntimeException("项目开发令:{"+n+"}在系统中不存在");
                }
            }else if (StrUtil.isBlank(n)){
                String projectName = map.get("项目名称");
                if (StrUtil.isNotBlank(projectName)){
                    Map<String, String> maps = rtProjectMapper.selectDevelopmentOrderByName(projectName);
                    if (maps==null){
                        throw new RuntimeException("项目{"+projectName+"}未查询到维护的开发令信息!");
                    }
                    map.put("项目开发令",maps.get("PROJECT_CODE"));

                }else {
                    throw new RuntimeException("导入年度计划表:项目开发令及项目名称不能都为空！");
                }
            }
        }

        List<String> projectNumberList = projectDataList.stream()
                .map(row -> row.get("项目名称"))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        if (projectNumberList.isEmpty()) {
            return Collections.emptyList();
        }

        // 并行校验
        Map<String, Boolean> nameExistsMap = projectNumberList.parallelStream()
                .collect(Collectors.toConcurrentMap(
                        name -> name,
                        name -> typpmProjectService.checkProjectName(name, 1),
                        (existing, replacement) -> existing
                ));

       // List<PlusTaskSaveDTO> plusTaskSaveDTOS = new ArrayList<>();
        List<Map<String, String>> duplicateProjectMaps = projectDataList.stream()
                .filter(map -> {
                    String projectName = map.get("项目名称");
                    // 条件：项目名非空 且 校验结果为false（重复）
                    return projectName != null && Boolean.FALSE.equals(nameExistsMap.get(projectName));
                })
                .collect(Collectors.toList());

        if (ObjectUtil.isEmpty(duplicateProjectMaps)){
            return Collections.emptyList();
        }

        List<String> codes = duplicateProjectMaps.stream().map(m -> m.get("项目开发令")).collect(
                Collectors.toList());

        List<Map<String, String>> maps = rtProjectMapper.selectDevelopmentOrder(codes);

        if (maps.size()!=codes.size()){
            List<String> cs = maps.stream().map(m -> m.get("PROJECT_CODE")).collect(Collectors.toList());
            codes.removeAll(cs);
            throw new RuntimeException("开发令:"+codes+"在系统中不存在！");
        }
        for (Map<String, String> row : duplicateProjectMaps) {

            List<PlusTaskSaveDTO> plusTaskSaveDTOS = new ArrayList<>();
            BigInteger planOid=rtProjectMapper.selectPrByName(row.get("项目名称"));

            //获取历史活动计划
            Map<String, Object> params = new HashMap();
            List<PlusTaskDTO> historyPlanList = typpmPlanActiveService.queryPlanActiveTreeByPlanOidAndOtype(planOid.toString(), TableTypeConstrant.TY_PLAN_OTYPE, params);
            Map<String, PlusTaskDTO> historyPlanMap = historyPlanList.stream()
                    .collect(Collectors.toMap(
                            PlusTaskDTO::getName,  // 假设名称是唯一标识
                            plan -> plan,
                            (existing, replacement) -> existing  // 名称重复时保留第一个（按业务处理）
                    ));

            //获取新的活动计划
            Map<String, String> newPlansMap  = extractColumnsBySuffix(row, "结束时间");

            //名称标准化校验
            List<String> names = rtTaskMapper.selectStandardName();
            if (CollUtil.isNotEmpty(names)){
                List<String> errNames = newPlansMap.keySet().stream().filter(f -> !names.contains(f)).collect(
                        Collectors.toList());
                if (CollUtil.isNotEmpty(errNames)){
                    throw new RuntimeException("不符合标准名称的项目计划："+JSONObject.toJSONString(errNames));
                }
            }
            //项目任务是否完成校验
            List<String> name = rtProjectMapper.selectFinishJobByName(row.get("项目名称"));
            if (CollUtil.isNotEmpty(name)){
                List<String> collect = newPlansMap.keySet().stream().filter(f -> name.contains(f)).collect(
                        Collectors.toList());
                if (CollUtil.isNotEmpty(collect)){
                    throw new RuntimeException("项目：{"+row.get("项目名称")+"}中包含已完成的项目计划："+JSONObject.toJSONString(collect));
                }
            }
            String lastEndDateSrt = row.get("项目开始时间");
            LocalDate projectStartDate = LocalDate.parse(lastEndDateSrt, RTPlmConstant.DATE_FORMATTER);
            projectStartDate = adjustToNextWorkday(projectStartDate); // 关键调整
            LocalDateTime projectStartDateTime = projectStartDate.atStartOfDay(); // 00:00:00
            String firstPlanStartTime = RTPlmConstant.DATETIME_FORMATTER.format(projectStartDateTime);

            boolean isFirstValidPlan = true;
            String previousEndTime = null;
            int planSequence = 1;

            for (Map.Entry<String, String> newPlanEntry : newPlansMap.entrySet()) {
                String newPlanName = newPlanEntry.getKey(); // 新计划名称（可能修改过）
                String newEndDateStr = newPlanEntry.getValue(); // 新计划结束时间
                if (StringUtils.isEmpty(newEndDateStr)) {
                    continue;
                }
                // 创建计划DTO
                PlusTaskSaveDTO plusTaskSaveDTO = new PlusTaskSaveDTO();

                if (historyPlanMap.containsKey(newPlanName)) {
                    // 操作：修改（基于历史计划更新）
                    PlusTaskDTO historyPlan = historyPlanMap.get(newPlanName);
                    BeanUtil.copyProperties(historyPlan, plusTaskSaveDTO);
                    historyPlanMap.remove(newPlanName);
                } else {
                    // 操作：新增（创建新计划）
                   // plusTaskSaveDTO = new PlusTaskSaveDTO();
                    // 新增计划的OID用雪花ID（如果需要）
                    plusTaskSaveDTO.setOid(snowflakeIdComponent.getInstance().nextId().toString());
                    plusTaskSaveDTO.setOtype(TableTypeConstrant.TY_PLAN_ACTIVITY_OTYPE);
                    // 父级信息（与历史计划一致）
                    plusTaskSaveDTO.setParentOID(planOid.toString());
                    plusTaskSaveDTO.setParentOTYPE(RTPlmConstant.PLAN_TYPE);
                    plusTaskSaveDTO.setActive("1");
                    plusTaskSaveDTO.setConstraintType("0");
                    plusTaskSaveDTO.setCritical("0");
                    plusTaskSaveDTO.setName(newPlanName); // 设置计划名称
                    plusTaskSaveDTO.setWork("0");
                    plusTaskSaveDTO.setPercentWorkComplete("0");
                   // plusTaskSaveDTO.setRiskType("UNAVAILABLE");
                    plusTaskSaveDTO.setJobOID(snowflakeIdComponent.getInstance().nextId().toString());
                    plusTaskSaveDTO.setHealthStatusType("UNAVAILABLE");
                    plusTaskSaveDTO.setControlled("0");
                    plusTaskSaveDTO.setMarkForDelete("0");
                    plusTaskSaveDTO.setEffortDriven("0");
                    plusTaskSaveDTO.setOwnerOID(TyAccountContext.getUser().getOid().toString());
                    plusTaskSaveDTO.setExecutorOID(TyAccountContext.getUser().getOid().toString());
                    BigInteger ownerGroupOid =rtProjectMapper.selectGroupByPlanId(planOid);
                  //  BigInteger ownerGroupOid = rtProjectMapper.selectGroupById(typpmProjectDTO.getOid());
                    plusTaskSaveDTO.setOwnerGroupOid(ownerGroupOid.toString());
                    plusTaskSaveDTO.setExecutorGroupOid(ownerGroupOid.toString());
                    plusTaskSaveDTO.setTypeOID("264465053392879616");
                    plusTaskSaveDTO.setTypeOTYPE(TableTypeConstrant.TY_TYPE_OTYPE);
                }


                // 处理当前计划的结束时间（转为 23:59:59 格式）
                LocalDate endDate = LocalDate.parse(newEndDateStr, RTPlmConstant.DATE_FORMATTER);
                endDate = adjustToNextWorkday(endDate); // 关键调整
                LocalDateTime endDateTime = endDate.atTime(23, 59, 59); // 23:59:59
                String currentEndTime = RTPlmConstant.DATETIME_FORMATTER.format(endDateTime);

                // 设置开始时间
                String currentStartTime;
              //  PlusTaskSaveDTO plusTaskSaveDTO=new PlusTaskSaveDTO();
                if (isFirstValidPlan) {
                    // 第一个计划：使用项目开始时间（00:00:00）
                    currentStartTime = firstPlanStartTime;
                    isFirstValidPlan = false;
                } else {
                    // 后续计划：使用前一个计划结束时间的下一秒（00:00:00）
                    LocalDateTime nextStartTime = LocalDateTime.parse(previousEndTime,
                                                                      RTPlmConstant.DATETIME_FORMATTER)
                            .plusSeconds(1);
                    LocalDate nextStartDate = nextStartTime.toLocalDate();
                    // 若开始日期是周末，调整到下周一的00:00:00
                    if (isWeekend(nextStartDate)) {
                        nextStartDate = adjustToNextWorkday(nextStartDate);
                        nextStartTime = nextStartDate.atStartOfDay();
                    }
                    currentStartTime = RTPlmConstant.DATETIME_FORMATTER.format(nextStartTime);
                    plusTaskSaveDTO.setConstraintType("4");
                }

                // 计算工期（毫秒）并设置
                long durationMillis = WorkdayCalculator.calculateWorkdayDuration(
                        currentStartTime,
                        currentEndTime
                );

                String finishDate = validateAndFixTimeRange(currentStartTime, currentEndTime);
                currentEndTime=finishDate;
                plusTaskSaveDTO.setOutLineLevel("1");
                plusTaskSaveDTO.setOutLineNumber(String.valueOf(planSequence++));
                plusTaskSaveDTO.setLineNumber(plusTaskSaveDTO.getOutLineNumber());// 设置序号
                plusTaskSaveDTO.setEarlyStart(currentStartTime);
                plusTaskSaveDTO.setConstraintDate(currentStartTime);
                plusTaskSaveDTO.setStartDate(currentStartTime);
                plusTaskSaveDTO.setDuration(String.valueOf(durationMillis));
                plusTaskSaveDTO.setEarlyFinish(currentEndTime); // 设置结束时间
                plusTaskSaveDTO.setFinishDate(currentEndTime); // 设置结束时间
                String lateFinish = formatLocalDateTime(LocalDateTime.parse(
                        Objects.requireNonNull(ExcelParser.getProjectEndDateWithTime(row, "结束时间")),
                        WorkdayCalculator.DATE_TIME_FORMATTER));
                plusTaskSaveDTO.setLateStart(formatLocalDateTime(WorkdayCalculator.calculateLateStartFromMillis(LocalDateTime.parse(lateFinish,WorkdayCalculator.DATE_TIME_FORMATTER),durationMillis)));
                plusTaskSaveDTO.setLateFinish(lateFinish);

                if (milestoneList.contains(newPlanEntry.getKey())) {
                    plusTaskSaveDTO.setMilestone("1");
                } else {
                    plusTaskSaveDTO.setMilestone("0");
                }
                // 记录当前计划的结束时间，供下一个计划使用
                previousEndTime = currentEndTime;

                //判断是否为null
                if(plusTaskSaveDTO.getActualStartDate()!=null){
                    plusTaskSaveDTO.setActualFinishDate(this.convertDateTimeFormat(plusTaskSaveDTO.getActualFinishDate()));
                }
                plusTaskSaveDTOS.add(plusTaskSaveDTO);


            }
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Map<String, TIMESTAMP> map=rtProjectMapper.selectPlanTimeById(planOid.toString());
            //获取最早时间
            String startDate = plusTaskSaveDTOS.stream().min((t1, t2) -> {
                try {
                    return sdf.parse(t1.getStartDate()).compareTo(sdf.parse(t2.getStartDate()));
                } catch (ParseException e) {
                    throw new RuntimeException(e);
                }
            }).get().getStartDate();
            //获取最晚时间
            String finishDate = plusTaskSaveDTOS.stream().max((t1, t2) -> {
                try {
                    return sdf.parse(t1.getFinishDate()).compareTo(sdf.parse(t2.getFinishDate()));
                } catch (ParseException e) {
                    throw new RuntimeException(e);
                }
            }).get().getFinishDate();
            PlanTime planTime=new PlanTime();
            planTime.setOid(planOid.toString());
            if (Timestamp.valueOf(startDate).compareTo(map.get("STARTDATE").timestampValue())<0){
                planTime.setStartTime(startDate);
            }
            if (Timestamp.valueOf(finishDate).compareTo(map.get("FINISHDATE").timestampValue())>0){
                planTime.setEndTime(finishDate);
            }
            if (!(StrUtil.isBlank(planTime.getStartTime()) && StrUtil.isBlank(planTime.getEndTime()))){
                rtProjectMapper.updatePlanTime(planTime);
            }
            //锁定
            rtProjectMapper.lockPlanById(planOid,TyAccountContext.getUser());
            List<PlusTaskSaveDTO> l = plusTaskSaveDTOS.stream().filter(
                    f -> Objects.nonNull(f.getActualFinishDate())).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(l)) {
                throw new RuntimeException("项目{"+row.get("项目名称")+"}"+"计划:"+l.stream().map(m->m.getName()).collect(
                        Collectors.toList())+"已完成！不可修改！");
            }
            //插入
            ganttChartService.saveTableTreeEx(planOid.toString(), plusTaskSaveDTOS);
           // updateGroupAndExecutor(planOid);
        }

        return duplicateProjectMaps;
    }

    private String validateAndFixTimeRange(String currentStartTime, String currentEndTime) {
        LocalDateTime startTime = LocalDateTime.parse(currentStartTime, RTPlmConstant.DATETIME_FORMATTER);
        LocalDateTime endTime = LocalDateTime.parse(currentEndTime, RTPlmConstant.DATETIME_FORMATTER);
        if (startTime.isAfter(endTime)) {
            // 将结束时间设置为开始时间当天的23:59:59
            endTime = startTime.toLocalDate().atTime(23, 59, 59);
        }
        return endTime.format(RTPlmConstant.DATETIME_FORMATTER);
    }


    public String formatLocalDateTime(LocalDateTime dateTime) {
        if (dateTime == null) {
            return null;
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        return dateTime.format(formatter);
    }

    @Override
    public RTProjectRelationVO queryProjectRelation(String projectOid, int pageNum, int pageSize) {
//        JobTreeQueryDTO queryDTO = new JobTreeQueryDTO();
        RTProjectRelationVO rtProjectRelationVO = new RTProjectRelationVO();
        ProjectEntityParamDTO params = new ProjectEntityParamDTO();
        params.setConditionJoint("and");
        params.setNeedCount(false);
        params.setNeedSort(true);
        params.setPageNum(pageNum);
        params.setPageSize(pageSize);
        SearchTypeDTO projectTypeDTO = RTPlmConstant.getSearchTypeDTO("PROJECT_TYPE");
        SearchTypeDTO planTypeDTO = RTPlmConstant.getSearchTypeDTO("PLAN_TYPE");
        List<SearchTypeDTO> typeList = new ArrayList<>();
        typeList.add(projectTypeDTO);
        typeList.add(planTypeDTO);
        params.setTypeList(typeList);
//        queryDTO.setProjectOid(projectOid);
        //获取当前项目的项目任务
        TyppmPlan plan = this.typpmPlanService.queryPlanDTOByParent(projectOid, TableTypeConstrant.TY_PROJECT_TYPE);
        if(plan==null){
            return null;
        }
        List<PlusTaskDTO> result = this.planActiveService.queryPlanActiveTreeByPlanOidAndOtype(plan.getOid(), TableTypeConstrant.TY_PLAN_OTYPE, null);
        rtProjectRelationVO.setCurJobs(result);
        Map<ProjectEntityVO, List<PlusTaskDTO>> jobMap = new HashMap<>();
        //获取所有项目列表
        PageInfo<ProjectEntityVO> pageInfo = this.typpmProjectService.queryProjectAdvancedSearch(params);
        for (ProjectEntityVO projectVO : pageInfo.getList()) {
//            JobTreeQueryDTO jobDTO = new JobTreeQueryDTO();
//            jobDTO.setProjectOid(projectVO.getOid().toString());
            List<PlusTaskDTO> plusTaskDTOS = this.planActiveService.queryPlanActiveTreeByPlanOidAndOtype(
                    String.valueOf(projectVO.getPlanOid()), TableTypeConstrant.TY_PLAN_OTYPE, null);
//            List<TyppmJobVO> jobTreeByParams = typpmJobService.getJobTreeByParams(jobDTO);
            jobMap.put(projectVO, plusTaskDTOS);
        }
        rtProjectRelationVO.setJobMap(jobMap);
        rtProjectRelationVO.setTotal(pageInfo.getTotal());
        return rtProjectRelationVO;
    }


    @Override
    public PageInfo<RTPreConstraintDO> queryProjectPreConstraint(String projectOid, int pageNum, int pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        List<RTPreConstraintDO> rtPreConstraintDOList = rtProjectMapper.selectPreconstraintById(projectOid);
        return new PageInfo<>(rtPreConstraintDOList);
    }


    @Override
    public void batchInsertOrUpdateProject(BatchOperationRequest<RTPreConstraintDO> request) {
        if (ObjectUtil.isEmpty(request) || CollectionUtil.isEmpty(request.getRecords())) {
            List<RTPreConstraintDO> records = request.getRecords();
            for (RTPreConstraintDO rtPreConstraintDO:records){
                BigInteger ajoid = rtPreConstraintDO.getAjoid();
                BigInteger bjoid = rtPreConstraintDO.getBjoid();
                 String ajState = rtProjectMapper.selectStateByJobOid(ajoid);
                 String bjState = rtProjectMapper.selectStateByJobOid(bjoid);
                 if ("已完成".equals(ajState)){
                     throw new RuntimeException("项目{"+rtPreConstraintDO.getApname()+"}任务:"+rtPreConstraintDO.getAjname()+"已完成！不允许关联！");
                 }else if("已完成".equals(bjState)){
                     throw new RuntimeException("项目{"+rtPreConstraintDO.getBpname()+"}任务:"+rtPreConstraintDO.getBjname()+"已完成！不允许关联！");
                 }

            }


            // 1. 获取项目ID
            String projectOid = request != null ? request.getProjectOid() : null;
            if (StringUtils.isNotBlank(projectOid) && existProject(projectOid)) {
                List<RTPreConstraintDO> existingRelations = rtProjectMapper.selectByProjectOid(projectOid);
                Set<BigInteger> affectedTasks = getAffectedTaskOids(existingRelations);
                rtProjectMapper.deleteRelations(projectOid);
                if (!affectedTasks.isEmpty()) {
                    updateTaskDescriptions(affectedTasks);
                }
            }
            return;
        }
        switch (request.getOperation()) {
            case "INSERT":
                batchInsert(request);
                break;
            case "UPDATE":
                batchUpdate(request);
                break;
            default:
                throw new IllegalArgumentException("不支持的操作类型: " + request.getOperation());
        }
    }

    private boolean existProject(String projectOid) {
        return rtProjectMapper.existProject(projectOid);
    }

    private void batchInsert(BatchOperationRequest<RTPreConstraintDO> request) {
        List<RTPreConstraintDO> records = request.getRecords();
        for (RTPreConstraintDO record : records) {
            record.setOid(snowflakeIdComponent.getInstance().nextId());
            TyplmEntityBaseUtil.fillCommonFieldForCreate(record);
        }
        rtProjectMapper.batchInsert(request.getRecords());
        // 获取受影响的主从任务OID
        Set<BigInteger> affectedTasks = getAffectedTaskOids(records);
        updateTaskDescriptions(affectedTasks);
    }


    private void batchUpdate(BatchOperationRequest<RTPreConstraintDO> request) {
        List<BigInteger> masterOids = new ArrayList<>();
        List<BigInteger> slaveOids = new ArrayList<>();
        for (RTPreConstraintDO record : request.getRecords()) {
            BigInteger ajoid = record.getAjoid();
            masterOids.add(ajoid);
            BigInteger bjoid = record.getBjoid();
            slaveOids.add(bjoid);
        }
        List<Job> masterDates = rtProjectMapper.batchSelectMasterStartDates(masterOids);
        Map<BigInteger, Date> masterStartMap = masterDates.stream()
                .collect(Collectors.toMap(
                        Job::getActivityID,
                        Job::getStartdate
                ));
        List<Job> slaveDates = rtProjectMapper.batchSelectSlaveFinishDates(slaveOids);
        Map<BigInteger, Date> slaveFinishMap = slaveDates.stream()
                .collect(Collectors.toMap(
                        Job::getActivityID,
                        Job::getFinishdate
                ));
        // 循环校验
        for (RTPreConstraintDO record : request.getRecords()) {
            BigInteger masterOid = record.getAjoid();
            BigInteger slaveOid = record.getBjoid();
            Date masterStart = masterStartMap.get(masterOid);
            Date slaveFinish = slaveFinishMap.get(slaveOid);
            if (ObjectUtil.isEmpty(masterOid)) {
                throw RTErrorCodeEnum.MASTER_NOT_EXIST.getException("主任务不存在");
            }
            if (ObjectUtil.isEmpty(slaveOid)) {
                throw RTErrorCodeEnum.SLAVE_NOT_EXIST.getException("从任务不存在");
            }
            LocalDateTime maStart = rtProjectMapper.selectMasterStartDates(masterOid);
            LocalDateTime slFinish = rtProjectMapper.selectSlaveFinishDates(slaveOid);
            // 从任务完成时间晚于主任务开始时间 → 报错
            if (slFinish.isAfter(maStart)) {
                throw RTErrorCodeEnum.SLAVE_AFTER_MASTER.getException("从任务完成时间晚于主任务开始时间,请重新设置");
            }
        }

        // 1. 获取当前所有关联关系
        List<RTPreConstraintDO> existingRelations = rtProjectMapper.selectByProjectOid(request.getProjectOid());
        // 2. 计算需要删除的旧关系
        List<BigInteger> toDelete = existingRelations.stream()
//                .filter(existing -> request.getRecords().stream()
//                        .noneMatch(newRec -> isSameRelation(existing, newRec)))
                .map(RTPreConstraintDO::getOid)
                .collect(Collectors.toList());
        // 3. 计算需要新增的关系
        List<RTPreConstraintDO> toInsert = request.getRecords();
        //.stream()
//                .filter(newRec -> existingRelations.stream()
//                        .noneMatch(existing -> isSameRelation(existing, newRec)))
        //        .collect(Collectors.toList());
        // 4. 执行增量更新
        if (!toDelete.isEmpty()) {
            rtProjectMapper.batchDeleteRelations(toDelete);
        }
        if (!toInsert.isEmpty()) {
            for (RTPreConstraintDO record : toInsert) {
                record.setOid(snowflakeIdComponent.getInstance().nextId());
                TyplmEntityBaseUtil.fillCommonFieldForCreate(record);
            }
            rtProjectMapper.batchInsert(toInsert);
        }
        // 5. 获取所有受影响的任务（包括删除和新增影响的）
        Set<BigInteger> affectedTasks = getAffectedTaskOids(existingRelations);
        affectedTasks.addAll(getAffectedTaskOids(request.getRecords()));
        // 6. 精准更新描述
        updateTaskDescriptions(affectedTasks);
    }

    // 判断两个关系记录是否相同
    private boolean isSameRelation(RTPreConstraintDO a, RTPreConstraintDO b) {
        return Objects.equals(a.getAjoid(), b.getAjoid()) &&
                Objects.equals(a.getBjoid(), b.getBjoid());
    }


    private void updateTaskDescriptions(Set<BigInteger> taskOids) {
        for (BigInteger taskOid : taskOids) {
            // 查询该任务作为主任务的所有关联
            List<RTPreConstraintDO> asMaster = rtProjectMapper.selectRelationsByMasterOid(taskOid);
            // 查询该任务作为从任务的所有关联
            List<RTPreConstraintDO> asSlave = rtProjectMapper.selectRelationsBySlaveOid(taskOid);
            // 构建合并后的描述
            String masterDesc = buildMasterTaskDescription(asMaster);
            String slaveDesc = buildSlaveTaskDescription(asSlave);
            // 组合完整描述
            StringBuilder fullDesc = new StringBuilder();
            if (StringUtils.isNotBlank(masterDesc))
                fullDesc.append(masterDesc);
            if (StringUtils.isNotBlank(slaveDesc)) {
                if (fullDesc.length() > 0) fullDesc.append(" | ");
                fullDesc.append(slaveDesc);
            }
            // 更新任务描述
            rtProjectMapper.updateTaskDescription(taskOid, fullDesc.toString());
            rtProjectMapper.updateActiveDescription(taskOid, fullDesc.toString());
        }
    }


    // 主任务描述构建
    private String buildMasterTaskDescription(List<RTPreConstraintDO> records) {
        if (CollectionUtil.isEmpty(records))
            return null;
        // 使用Set去重
        Set<String> slaveProjects = new LinkedHashSet<>();
        for (RTPreConstraintDO record : records) {
            if (record.getBpname() != null && record.getBjname() != null) {
                slaveProjects.add(record.getBpname() + "-" + record.getBjname());
            }
        }
        if (slaveProjects.isEmpty())
            return null;
        return "从属项目: " + String.join("; ", slaveProjects);
    }

    // 从任务描述构建
    private String buildSlaveTaskDescription(List<RTPreConstraintDO> records) {
        if (CollectionUtil.isEmpty(records))
            return null;
        // 使用Set去重
        Set<String> masterProjects = new LinkedHashSet<>();
        for (RTPreConstraintDO record : records) {
            if (record.getApname() != null && record.getAjname() != null) {
                masterProjects.add(record.getApname() + "-" + record.getAjname());
            }
        }
        if (masterProjects.isEmpty())
            return null;
        return "主项目:" + String.join(";", masterProjects);

    }

    private Set<BigInteger> getAffectedTaskOids(List<RTPreConstraintDO> records) {
        Set<BigInteger> affectedOids = new HashSet<>();
        for (RTPreConstraintDO record : records) {
            if (record.getAjoid() != null)
                affectedOids.add(record.getAjoid());
            if (record.getBjoid() != null)
                affectedOids.add(record.getBjoid());
        }
        return affectedOids;
    }

    //项目周期比对导出
    @Override
    public void exportProjectPlanExcel(HttpServletResponse response, String objectId) {

        BigInteger planIds = rtProjectsTemplateMapper.getPlanById(objectId);
        if (Objects.isNull(planIds)) {
            throw SystemErrorCodeEnum.ENTITY_NONE.message(new Object[]{"查询不到项目"});
        }
        String planOid = String.valueOf(planIds);
        TyppmPlan typpmPlan = typpmPlanService.selectOneByOID(planOid);

        if (Objects.isNull(typpmPlan)) {
            throw com.hustcad.plm.pdm.core.handle.exception.SystemErrorCodeEnum.ENTITY_NONE.message(
                    new Object[]{"项目计划"});
        } else {

            Map<String, Object> params = new HashMap<>();
            params.put("rootOID", planOid);
            params.put("rootOTYPE", "ty.projectmng.admin.CTyPlan");
            params.put("markfordelete", 0L);
            //当前计划
            List<TyppmActiveTreeDTO> treeDTOList = typpmPlanActiveService.queryActiveTreeDTOListByParams(params);
            if (CollUtil.isEmpty(treeDTOList)) {
                throw com.hustcad.plm.pdm.core.handle.exception.SystemErrorCodeEnum.DATA_FOUND.message(
                        new Object[]{"任务数据"});
            } else {
                //排序去重
                treeDTOList = treeDTOList.stream()
                        .sorted(Comparator.comparing(TyppmActiveTreeDTO::getLineNumber))
                        .distinct()
                        .collect(Collectors.toList());
                String oid = "";
                List<TyppmActiveTreeDTO> treeDTOListActive = null;
                List<RTTraverseParentDO> traverseParentList = traverseParentMapper.selectTraverseParent(planOid);
                if (CollUtil.isNotEmpty(traverseParentList)) {

                    //向上递归遍历树直到拿到项目计划id
                    RTTraverseParentDO traverseParent = traverseParentList.get(0);
                    List<TyppmPlanActive> typpmPlanActives = null;
                    int sum = 1;
                    while (true) {

                        typpmPlanActives = typpmPlanActiveService.queryParentPlanActiveByOid(
                                sum == 1 ? traverseParent.getTemplatePlanId() : oid);
                        if (CollUtil.isEmpty(typpmPlanActives)) {
                            break;
                        }
                        TyppmPlanActive typpmPlanActive = typpmPlanActives.get(0);
                        oid = typpmPlanActive.getParentOID();
                        sum++;

                    }
                    params.put("rootOID", oid);

                    //模板计划
                    treeDTOListActive = typpmPlanActiveService.queryActiveTreeDTOListByParams(params);
                    treeDTOListActive =  treeDTOListActive.stream()
                            .sorted(Comparator.comparing(TyppmActiveTreeDTO::getLineNumber))
                            .distinct()
                            .collect(Collectors.toList());

                }

                if (CollUtil.isEmpty(treeDTOListActive)) {
                    log.error("没有查询到模板计划");
                }

                //初始计划
                BaseLinePlanDTO baseLinePlanDTO = new BaseLinePlanDTO();
                baseLinePlanDTO.setParentOID(BigInteger.valueOf(Long.parseLong(objectId)));
                baseLinePlanDTO.setParentOTYPE("ty.inteplm.project.CTyProject");
                baseLinePlanDTO.setMarkfordelete(0L);
                baseLinePlanDTO.setBaselineName(RTPlmConstant.INITIAL_PLAN);
                List<BaselinePlan> baselinePlans = baseLinePlanExpandMapper.selectBaselinePlanByBaseLineName(
                        baseLinePlanDTO);

                if (CollUtil.isEmpty(baselinePlans)) {
                    throw com.hustcad.plm.pdm.core.handle.exception.SystemErrorCodeEnum.DATA_FOUND.message(
                            new Object[]{"初始计划"});
                }

                //查询初始计划树
                Map<String, Object> trees = new HashMap();
                trees.put("rootOID", String.valueOf(baselinePlans.get(0).getOid()));
                trees.put("rootOTYPE", "ty.projectmng.plan.CTyBaselinePlan");
                trees.put("markfordelete", 0L);
                List<TyppmActiveTreeDTO> activeTreeDTOList = this.baselinePlanActivityService.queryBaselinePlanActiveTreeByPlan(
                        trees);

                if (CollUtil.isEmpty(activeTreeDTOList)) {
                    throw com.hustcad.plm.pdm.core.handle.exception.SystemErrorCodeEnum.DATA_FOUND.message(
                            new Object[]{"初始计划计划树"});
                }

                //遍历子树创建新数组
                List<TyppmActiveTreeDTO> typpmActiveTreeDTOS = new ArrayList<>();
                for (int i = 0; i < activeTreeDTOList.size(); i++) {

                    activeTreeDTOList.get(i).setOutLineNumber(String.valueOf(i + 1));
                    typpmActiveTreeDTOS.add(activeTreeDTOList.get(i));

                    this.processChildren(activeTreeDTOList.get(i).getChildren(), String.valueOf(i + 1), typpmActiveTreeDTOS);
                }


                DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
                String fileName = typpmPlan.getName() + "_" + LocalDateTime.now().format(dtf);
                // 设置响应头
                response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
                response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".xlsx");


                try (Workbook workbook = new XSSFWorkbook()) {

                    if (CollUtil.isNotEmpty(treeDTOListActive)) {
                        createSheet(workbook, "模板计划对比", "模板计划", "初始计划", treeDTOListActive,
                                         typpmActiveTreeDTOS);
                    }

                        createSheet(workbook, "初始计划对比", "初始计划", "当前计划", typpmActiveTreeDTOS,
                                     treeDTOList);

                    workbook.write(response.getOutputStream());

                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    // 递归处理子节点
    private void processChildren(List<TyppmActiveTreeDTO> children, String parentOutline,
                                 List<TyppmActiveTreeDTO> resultList) {
        if (children == null || children.isEmpty()) return;

        int count = 1;
        for (TyppmActiveTreeDTO child : children) {
            child.setOutLineNumber(parentOutline + "." + count);
            resultList.add(child);

            processChildren(child.getChildren(), child.getOutLineNumber(), resultList);
            count++;
        }
    }

    //创建excel
    public void createSheet(Workbook workbook, String sheetName, String sheetOne, String sheetTwo,
                                   List<TyppmActiveTreeDTO> activeSheetOne, List<TyppmActiveTreeDTO> activeSheetTow) {


        List<ExportPlanExcelDTO> exportPlanExcelDTOS = compareList(activeSheetOne, activeSheetTow);


        Sheet sheet = workbook.createSheet(sheetName);

        autoSizeColumns(sheet, 16);

        CellStyle head = createHead(workbook);

        Row headerRow = sheet.createRow(0);

        sheet.addMergedRegion(new CellRangeAddress(0, 0, 3, 8));

        Cell mainHeaderCell1 = headerRow.createCell(3);
        mainHeaderCell1.setCellValue(sheetOne);
        mainHeaderCell1.setCellStyle(head);

        sheet.addMergedRegion(new CellRangeAddress(0, 0, 9, 14));

        Cell mainHeaderCell2 = headerRow.createCell(9);
        mainHeaderCell2.setCellValue(sheetTwo);
        mainHeaderCell2.setCellStyle(head);

        Row subHeaderRow = sheet.createRow(1);
        List<String> headers = Arrays.asList("序号", "层级", "WBS编号",
                                             "负责人", "计划名称", "计划开始时间", "计划结束时间", "前置任务", "工期",
                                             "负责人", "计划名称", "计划开始时间", "计划结束时间", "前置任务", "工期",
                                             "工期比较");

        CellStyle subHeaderStyle = createDataCellStyle(workbook);

        for (int i = 0; i < headers.size(); i++) {
            Cell cell = subHeaderRow.createCell(i);
            cell.setCellValue(headers.get(i));
            cell.setCellStyle(subHeaderStyle);
        }

        int plan = sheetOne.equals("当前计划") ? 1 : (sheetTwo.equals("当前计划") ? 1 : 0);

        fillData(workbook, sheet, exportPlanExcelDTOS,plan);

    }

    // 自动调整列宽
    private  void autoSizeColumns(Sheet sheet, int columnCount) {

        for (int i = 0; i < columnCount; i++) {
            sheet.autoSizeColumn(i);
        }
        for (int i = 0; i < columnCount; i++) {
            int currentWidth = sheet.getColumnWidth(i);
            int minWidth = 20 * 256;
            if (currentWidth < minWidth) {
                sheet.setColumnWidth(i, minWidth);
            }
        }
    }

    //表头样式
    private CellStyle createDataCellStyle(Workbook workbook) {
        CellStyle style = workbook.createCellStyle();
        style.setBorderBottom(BorderStyle.THIN);
        style.setBorderTop(BorderStyle.THIN);
        style.setBorderLeft(BorderStyle.THIN);
        style.setBorderRight(BorderStyle.THIN);
        return style;
    }

    //表格样式
    private CellStyle createHead(Workbook workbook) {
        Font boldFont = workbook.createFont();
        boldFont.setBold(true);
        CellStyle subHeaderStyle = workbook.createCellStyle();
        subHeaderStyle.setFont(boldFont);
        subHeaderStyle.setBorderTop(BorderStyle.THIN);
        subHeaderStyle.setBorderBottom(BorderStyle.THIN);
        subHeaderStyle.setBorderLeft(BorderStyle.THIN);
        subHeaderStyle.setBorderRight(BorderStyle.THIN);
        subHeaderStyle.setAlignment(HorizontalAlignment.CENTER);
        return subHeaderStyle;

    }

    //按层级排序
    public  List<ExportPlanExcelDTO> sortByOutlineNumber(List<ExportPlanExcelDTO> list) {
        return list.stream()
                .sorted((dto1, dto2) -> {
                    String[] parts1 = dto1.getLevel().split("\\.");
                    String[] parts2 = dto2.getLevel().split("\\.");

                    int minLength = Math.min(parts1.length, parts2.length);


                    for (int i = 0; i < minLength; i++) {
                        int num1 = Integer.parseInt(parts1[i]);
                        int num2 = Integer.parseInt(parts2[i]);

                        if (num1 != num2) {
                            return Integer.compare(num1, num2);
                        }
                    }
                    // 前缀相同时，层级少的排在前面
                    return Integer.compare(parts1.length, parts2.length);
                })
                .collect(Collectors.toList());
    }


    //list转map
    public  Map<String, TyppmActiveTreeDTO> listTurnMap(List<TyppmActiveTreeDTO> listActive) {

        Map<String, TyppmActiveTreeDTO> linkedMap = new LinkedHashMap<>();

        for (TyppmActiveTreeDTO tyActiveOne : listActive) {

            linkedMap.put(tyActiveOne.getOutLineNumber(), tyActiveOne);

        }
        return linkedMap;
    }

    //比较list
    public  List<ExportPlanExcelDTO> compareList(List<TyppmActiveTreeDTO> activeOne,
                                                       List<TyppmActiveTreeDTO> activeTow) {


        Map<String, TyppmActiveTreeDTO> tyActiveMapOne = listTurnMap(activeOne);

        Map<String, TyppmActiveTreeDTO> tyActiveMapTow = listTurnMap(activeTow);


        CompareResult result = new CompareResult();

        // 识别删除的节点
        tyActiveMapOne.keySet().stream()
                .filter(key -> !tyActiveMapTow.containsKey(key))
                .forEach(key -> result.setDeletedNodes(tyActiveMapOne.get(key)));

        //  识别新增的节点
        tyActiveMapTow.keySet().stream()
                .filter(key -> !tyActiveMapOne.containsKey(key))
                .forEach(key -> result.setAddedNodes(tyActiveMapTow.get(key)));

        List<TreeComparatorUtil.MatchedPair> matchedPairs = TreeComparatorUtil.findMatchedNodes(tyActiveMapOne, tyActiveMapTow);

        for (TreeComparatorUtil.MatchedPair matchedPair : matchedPairs) {

            result.setOldAndNewFit(matchedPair.getOldNode(), matchedPair.getNewNode());
        }

        // 识别相同的节点不同name
        tyActiveMapOne.keySet().stream()
                .filter(tyActiveMapTow::containsKey)//key相同
                .filter(key -> !isNodeEqual(tyActiveMapOne.get(key), tyActiveMapTow.get(key)))//值不同
                .filter(key -> isNotEqual(tyActiveMapOne.get(key), tyActiveMapTow.get(key),matchedPairs))//name没有存在于matchedPairs
                .forEach(key -> result.setOldAndNewNotFit(tyActiveMapOne.get(key), tyActiveMapTow.get(key)));


        return result.getExportPlanExcelDTOList();
    }

    //比较name和层级
    private  boolean isNodeEqual(TyppmActiveTreeDTO node1, TyppmActiveTreeDTO node2) {
        return Objects.equals(node1.getName(), node2.getName()) && Objects.equals(node1.getOutLineNumber(), node2.getOutLineNumber());
    }

    //在相同name组中不存在
    private  boolean isNotEqual(TyppmActiveTreeDTO node1, TyppmActiveTreeDTO node2,List<TreeComparatorUtil.MatchedPair> matchedPairs) {

        for (TreeComparatorUtil.MatchedPair matchedPair : matchedPairs) {

            if (node1.getName().equals(matchedPair.getOldNode().getName()) || node2.getName().equals(matchedPair.getNewNode().getName())) {
                return false;
            }

        }
        return true;
    }



    private  void fillData(Workbook workbook, Sheet sheet, List<ExportPlanExcelDTO> activeOne, int plan) {

        List<ExportPlanExcelDTO> typpmActiveTreeDTOS = sortByOutlineNumber(activeOne);


        CellStyle dataStyle = createDataCellStyle(workbook);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        int rowNum = 2;
        int index = 1;


        for (ExportPlanExcelDTO typpmActiveTreeDTO : typpmActiveTreeDTOS) {

            Row row = sheet.createRow(rowNum++);
            Cell idCell = row.createCell(0);
            idCell.setCellValue(index);
            idCell.setCellStyle(dataStyle);
            index++;
            long day = 0;
            long days = 0;

            Cell outLineNumber = row.createCell(1);
            outLineNumber.setCellValue(typpmActiveTreeDTO.getLevel());
            outLineNumber.setCellStyle(dataStyle);

            Cell wbs = row.createCell(2);
            wbs.setCellValue(typpmActiveTreeDTO.getWbsCode());
            wbs.setCellStyle(dataStyle);

            Cell ownerName = row.createCell(3);
            ownerName.setCellValue(typpmActiveTreeDTO.getPrincipal());
            ownerName.setCellStyle(dataStyle);


            Cell name = row.createCell(4);
            name.setCellValue(typpmActiveTreeDTO.getPlanName());
            name.setCellStyle(dataStyle);


            Cell earlyStart = row.createCell(5);
            if (null!=typpmActiveTreeDTO.getPlanStartTime()) {
                earlyStart.setCellValue(formatter.format(typpmActiveTreeDTO.getPlanStartTime()));
            }
            earlyStart.setCellStyle(dataStyle);

            Cell earlyFinish = row.createCell(6);
            if (null!=typpmActiveTreeDTO.getPlanEndTime()) {
                earlyFinish.setCellValue(formatter.format(typpmActiveTreeDTO.getPlanEndTime()));
            }
            earlyFinish.setCellStyle(dataStyle);

            Cell beforeLineNumber = row.createCell(7);
            beforeLineNumber.setCellValue(typpmActiveTreeDTO.getPredecessorTask());
            beforeLineNumber.setCellStyle(dataStyle);

            if (1==plan){

                if (1==typpmActiveTreeDTO.getExecutedStatus()){
                    days=calculateWorkdaysBetween(typpmActiveTreeDTO.getPlanStartTime(), typpmActiveTreeDTO.getPlanEndTime());
                }else if (2==typpmActiveTreeDTO.getExecutedStatus()){
                    days = Duration.between(LocalDateTime.now(), typpmActiveTreeDTO.getPlanStartTime()).toDays();
                }

            }else {
                days = calculateWorkdaysBetween(typpmActiveTreeDTO.getPlanStartTime(), typpmActiveTreeDTO.getPlanEndTime());
            }

            days = calculateWorkdaysBetween(typpmActiveTreeDTO.getPlanStartTime(), typpmActiveTreeDTO.getPlanEndTime());

            Cell period = row.createCell(8);
            period.setCellValue(days);
            period.setCellStyle(dataStyle);


            Cell activeData1 = row.createCell(9);

            activeData1.setCellValue(typpmActiveTreeDTO.getPrincipalAgain());
            activeData1.setCellStyle(dataStyle);

            Cell activeData2 = row.createCell(10);
            activeData2.setCellValue(typpmActiveTreeDTO.getPlanNameAgain());
            activeData2.setCellStyle(dataStyle);

            Cell activeData3 = row.createCell(11);
            if (null!=typpmActiveTreeDTO.getPlanStartTimeAgain()) {
                activeData3.setCellValue(formatter.format(typpmActiveTreeDTO.getPlanStartTimeAgain()));
            }
            activeData3.setCellStyle(dataStyle);

            Cell activeData4 = row.createCell(12);
            if (null!=typpmActiveTreeDTO.getPlanEndTimeAgain()) {
                activeData4.setCellValue(formatter.format(typpmActiveTreeDTO.getPlanEndTimeAgain()));
            }
            activeData4.setCellStyle(dataStyle);

            Cell activeData5 = row.createCell(13);
            activeData5.setCellValue(typpmActiveTreeDTO.getPredecessorTaskAgain());
            activeData5.setCellStyle(dataStyle);

            if (1==plan){

                if (1==typpmActiveTreeDTO.getExecutedStatus()){
                    day=calculateWorkdaysBetween(typpmActiveTreeDTO.getPlanStartTime(), typpmActiveTreeDTO.getPlanEndTime());
                }else if (2==typpmActiveTreeDTO.getExecutedStatus()){
                    day = Duration.between(LocalDateTime.now(), typpmActiveTreeDTO.getPlanStartTime()).toDays();
                }

            }else {
                day = calculateWorkdaysBetween(typpmActiveTreeDTO.getPlanStartTime(), typpmActiveTreeDTO.getPlanEndTime());
            }


            day = calculateWorkdaysBetween(typpmActiveTreeDTO.getPlanEndTimeAgain(), typpmActiveTreeDTO.getPlanEndTimeAgain());

            Cell perio = row.createCell(14);
            perio.setCellValue(day);
            perio.setCellStyle(dataStyle);

            Cell difference = row.createCell(15);


            String sum = "";
            sum = String.valueOf(day - days);

            if ( null == typpmActiveTreeDTO.getPlanStartTime() || null == typpmActiveTreeDTO.getPlanStartTimeAgain() || null == typpmActiveTreeDTO.getPlanEndTime() || null == typpmActiveTreeDTO.getPlanEndTimeAgain() ) {
                sum = "";
            }

            difference.setCellValue(sum);
            difference.setCellStyle(dataStyle);

        }
    }

    //计算两个日期间的工期
    public static long calculateWorkdaysBetween(LocalDateTime start, LocalDateTime end) {


        if (null == start || null == end) {
            return 0;
        }


        if (start.isAfter(end)) {
            LocalDateTime temp = start;
            start = end;
            end = temp;
        }

        long workingDays = 0;
        LocalDate currentDate = start.toLocalDate();
        LocalDate endDate = end.toLocalDate();

        while (!currentDate.isAfter(endDate)) {
            DayOfWeek dayOfWeek = currentDate.getDayOfWeek();
            if (dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY) {
                workingDays++;
            }
            currentDate = currentDate.plusDays(1);
        }

        return workingDays;

    }
    // 添加安全转换方法
    private String convertToString(Object value) {
        if (value == null) {
            return "";
        }
        if (value instanceof String) {
            return (String) value;
        }
        if (value instanceof BigDecimal) {
            return ((BigDecimal) value).toPlainString();
        }
        if (value instanceof Number) {
            return value.toString();
        }
        return value.toString();
    }

//    public void saveAsProject1(TyppmProjectDTO project) {
//        String oldProjectOID = project.getOldProjectOID();
//        String developmentOrder = project.getDevelopmentOrder();
//        String projectName = rtProjectMapper.selectProjectNameByProjectOid(oldProjectOID);
//        Map<String, String> stringStringMap = rtProjectMapper.selectDevelopmentOrderByOrderName(developmentOrder);
//        List<String> isExist=rtProjectMapper.selectOrderIsExist("717552793724518400",developmentOrder);
//        if (isExist.size()>0){
//            throw RTErrorCodeEnum.PROJECT_ORDER_ERROR.message("此开发令的项目已存在");
//        }
//        if(!stringStringMap.get("RESPONSIBLE_PERSON").toString().equals(TyAccountContext.getUser().getFullname())){
//            throw RTErrorCodeEnum.PROJECT_PERSON_ERROR.message("开发令负责人不能参考创建其他人的项目");
//        }
//        typpmProjectService.insertProject(project,true);
//
//    }

    @Override
    public void saveAsProject(TyppmProjectDTO project) {

        if (project.getStartDate().plusDays(1).isBefore(LocalDateTime.now())){
            throw RTErrorCodeEnum.PROJECT_DATE_ERROR.message("项目开始时间必须晚于今天");
        }
        String oldProjectOID = project.getOldProjectOID();
        String developmentOrder = project.getDevelopmentOrder();
        String projectName = rtProjectMapper.selectProjectNameByProjectOid(oldProjectOID);
        Map<String, String> stringStringMap = rtProjectMapper.selectDevelopmentOrderByOrderName(developmentOrder);
        List<String> isExist=rtProjectMapper.selectOrderIsExist("717552793724518400",developmentOrder);
        if (isExist.size()>0){
            throw RTErrorCodeEnum.PROJECT_ORDER_ERROR.message("此开发令的项目已存在");
        }
        if(!stringStringMap.get("RESPONSIBLE_PERSON").toString().equals(TyAccountContext.getUser().getFullname())){
            throw RTErrorCodeEnum.PROJECT_PERSON_ERROR.message("开发令负责人不能参考创建其他人的项目");
        }

        // 生成项目
        Map<String, Object> projectMap = rtProjectMapper.selectProjectByOid(oldProjectOID);
        Map<String, String> projectStringMap =new HashMap<>();

        projectStringMap.put("developmentOrder",developmentOrder);

        if (projectMap != null) {
            projectMap.entrySet().forEach(entry -> {
                // 添加空值检查
                if (entry != null && entry.getValue() != null) {
                    // 安全转换为字符串，处理null值和BigDecimal类型
                    String value = convertToString(entry.getValue());
                    projectStringMap.put(entry.getKey(), value);
                } else {
                    // 处理空值情况
                    projectStringMap.put(entry.getKey(), "");
                }
            });
        }

        // 添加对 projectStringMap 的空值检查
        ProjectVO projectVO = buildProjectsVO(projectStringMap, stringStringMap);
        BigInteger planOID = rtProjectMapper.selectPlanOIDByProjectOID(oldProjectOID);
        TyppmProjectDTO map = BeanMapper.map(projectVO, TyppmProjectDTO.class);
        if (CollUtil.isNotEmpty(stringStringMap)) {
            //项目名称
            map.setTypeOID(projectVO.getTypeOid());
            map.setTypeOTYPE(projectVO.getTypeOtype());
            map.setPercentWorkComplete((double) 0);
            map.setTypeLogicalIdentifier(projectVO.getTypeLogicalIdentifier());
            map.setTypeName(projectVO.getTypeName());
            Map<String, List<String>> ibaAttribute = map.getIbaAttribute();
            ibaAttribute.put("encryptionlevel",Arrays.asList("非密"));
            ibaAttribute.put("DevelopmentOverview",Arrays.asList(projectStringMap.get("开发概要")));
            ibaAttribute.put("ProjectScction", Arrays.asList(projectStringMap.get("CATEGORY")));
            ibaAttribute.put("MainCategory/Client", Arrays.asList("大客户"));
            map.setName(project.getName());
            map.setDevelopmentLevel(stringStringMap.get("DEV_LEVEL"));
            map.setIbaAttribute(new HashMap<String, List<String>>());
            map.setName(project.getName());
            map.setPhase("编制中");
            //开发令号
            if (StrUtil.isNotBlank(stringStringMap.get("DEV_ORDER_NUMBER"))){
                ibaAttribute.put("DevelopmentOrder",Arrays.asList(stringStringMap.get("DEV_ORDER_NUMBER")));
            }
            if (StrUtil.isNotBlank(projectVO.getCategory())){
                project.setCategory(projectVO.getCategory().toString());
                project.setCategoryName(projectVO.getCategory().toString());
            }
            //项目开发令
            if (StrUtil.isNotBlank(stringStringMap.get("DEV_CATEGORY"))){
                ibaAttribute.put("Grade",Arrays.asList(stringStringMap.get("DEV_CATEGORY")));
            }
            //机型号
            if (StrUtil.isNotBlank(stringStringMap.get("AIRCRAFT_MODEL"))){
                ibaAttribute.put("productType",Arrays.asList(stringStringMap.get("AIRCRAFT_MODEL")));
            }
            //负责人
            if (StrUtil.isNotBlank(stringStringMap.get("RESPONSIBLE_PERSON"))){
                ibaAttribute.put("ProjectManager",Arrays.asList(stringStringMap.get("RESPONSIBLE_PERSON")));
            }
            //开发分级
            if (StrUtil.isNotBlank(stringStringMap.get("DEV_CATEGORY"))){
                ibaAttribute.put("DevelopmentLevel",Arrays.asList(stringStringMap.get("DEV_CATEGORY")));
            }
            //开发类型
            if (StrUtil.isNotBlank(stringStringMap.get("SECTOR"))){
                ibaAttribute.put("DevelopmentType",Arrays.asList(stringStringMap.get("DEVELOPMENT_TYPE")));
            }else {
                ibaAttribute.put("DevelopmentType",Arrays.asList("常规"));
            }
            //开发等级
            if (StrUtil.isNotBlank(stringStringMap.get("SECTOR"))){
                ibaAttribute.put("Grade",Arrays.asList(stringStringMap.get("DEV_LEVEL")));
            }
            //产品系列
            if (StrUtil.isNotBlank(stringStringMap.get("PROJECT_SERIES"))){
                ibaAttribute.put("ProductSeries",Arrays.asList(stringStringMap.get("PROJECT_SERIES")));
            }
            //产品类别
            if (StrUtil.isNotBlank(stringStringMap.get("PROJECT_CATEGORY"))){
                ibaAttribute.put("ProductCategory",Arrays.asList(stringStringMap.get("PROJECT_CATEGORY")));
            }else {
                ibaAttribute.put("ProductCategory",Arrays.asList("常规分类"));
            }
            //系数
            if (StrUtil.isNotBlank(stringStringMap.get("COEFFICIENT"))){
                ibaAttribute.put("Coefficient",Arrays.asList(stringStringMap.get("COEFFICIENT")));
            }

            if (StrUtil.isNotBlank(stringStringMap.get("大类/客户"))){
                ibaAttribute.put("MainCategory/Client",Arrays.asList(stringStringMap.get("大类/客户")));
            }
            if (StrUtil.isNotBlank(stringStringMap.get("PROJECT_SERIES"))){
                ibaAttribute.put("ProjectSection",Arrays.asList(stringStringMap.get("PROJECT_SERIES")));
            }else {
                ibaAttribute.put("ProjectSection",Arrays.asList("常规项目"));
            }
            if (StrUtil.isNotBlank(stringStringMap.get("变更备注"))){
                ibaAttribute.put("ChangeRemarks",Arrays.asList(stringStringMap.get("变更备注")));
            }
            stringStringMap.put("项目经理",TyAccountContext.getUser().getFullname());
            //ibaAttribute.put("项目开发令",Arrays.asList(stringStringMap.get("PROJECT_CODE")));
            //推进方式
            ibaAttribute.put("PushMode",Arrays.asList(stringStringMap.get("BOOST")));
            //ibaAttribute.put("DevelopmentOrder",Arrays.asList(stringStringMap.get("PROJECT_CODE")));
            int year = LocalDateTime.now().getYear();
            ibaAttribute.put("Year",Arrays.asList(year+"年度"));
            project.setIbaAttribute(ibaAttribute);

        }else {
            throw new RuntimeException("项目名:"+projectName+"不存在");
        }
        if (Boolean.TRUE.equals(project.isCheckFlag())) {
            typpmProjectService.checkProjectNumber(project);
            typpmProjectService.checkProjectName(project);
        }
        setProjectFixedDefaults(project);
        setUserRelatedProperties(project);
        setContainerAndDomainInfo(project);
        setTypeAndManagementInfo(project);
        setUpdatorInfo(project);
        setSourceProjectPlan(project);
        project.setName(stringStringMap.get("PROJECT_NAME"));
        if (planOID != null) {
            BigInteger templateOid = rtProjectMapper.selectTemplateByPlanOID(planOID);
            if (templateOid != null) {
                Map<String, String> templateMap = rtProjectMapper.selectMapByOid(templateOid);
                if (templateMap != null) {
                    projectVO.setPlanTemplateName(templateMap.get("NAME"));
                    projectVO.setPlanTemplateOID(templateMap.get("OID"));
                    projectVO.setPlanTemplateOTYPE(templateMap.get("OTYPE"));
                }
            }
        }else {
            throw RTErrorCodeEnum.PROJECT_NOT_FOUND.getException("项目缺少计划");
        }
        //生成项目编码

        String projectCode = projectCodeGenerator.generateProjectCode();
        if (projectCode != null) {
            project.setProjectNumber(projectCode);
        }
        project.setOid(snowflakeIdComponent.getInstance().nextId().toString());
        //保存项目
        //typpmProjectService.insertProject(project,true);

        TyppmPlan plan = project.getPlan();
        //修改计划时间
        LocalDate old = plan.getStartDate().toLocalDate();
        LocalDate neu=project.getStartDate().toLocalDate();
        LocalDateTime startDate = plan.getStartDate();
        LocalDateTime finishDate = plan.getFinishDate();
        project.setStartDate(startDate);
        project.setFinishDate(finishDate);
        typpmProjectService.saveAsProject(project);
//        project.getIbaAttribute().put("ProjectDevelopmentOrder",Arrays.asList(project.getDevelopmentOrder()));
//        project.setContainerOid("460528473");
//        projectServiceForPdm.insertProject(project);
        Map<String, String> hashMap = new HashMap();
        hashMap.put("pdmProjectOid", project.getOid());
        hashMap.put("pdmProjectOtype", project.getOtype());
        typpmPlanService.updatePlan(plan);
        handlerRemoteService.syncSaveIbaAttributeByCreateProject(project, hashMap);
        fillCommonProjectData(project);

        //导入团队
        List<Map<String, Object>> projectDirectorList = new ArrayList<>();
        List<ProjectTeamTreeDTO> projectTeamTreeDTOS = typpmProjectTeamService.queryContainerTeamByContainer(
                project.getOldProjectOID(), "ty.inteplm.project.CTyProject");
        String linkId = snowflakeIdComponent.getInstance().nextId().toString();
        for (ProjectTeamTreeDTO projectTeamTreeDTO : projectTeamTreeDTOS) {
            if (projectTeamTreeDTO.getType().equals("PROJECT DIRECTOR")){
                List<TyppmTeamTreeDTO> children=new ArrayList<>();
                List<TyppmTeamTreeDTO> child = projectTeamTreeDTO.getChildren();
                boolean flag=false;
                for (TyppmTeamTreeDTO typpmTeamTreeDTO : child) {
                    String name = typpmTeamTreeDTO.getFullName();
                    Map<String, Object> pmMap = new HashMap<>();
                    UserDO userDO = rtProjectMapper.selectUserByName(name);
                    if (userDO != null) {
                        pmMap.put("projectOid", project.getOid());
                        pmMap.put("roleName", "PROJECT DIRECTOR");
                        pmMap.put("userOid", userDO.getOid());
                    }
                    if (!name.equals(TyAccountContext.getUser().getFullname())) {
                        typpmProjectService.deleteProjectManagerOrAdministrator(pmMap);
                    }else {
                        flag=true;
                    }
                }
                if (StrUtil.isNotBlank(stringStringMap.get("RESPONSIBLE_PERSON"))){
                    String name = stringStringMap.get("RESPONSIBLE_PERSON");
                    UserDO userDO = rtProjectMapper.selectUserByName(name);
                    TyppmTeamTreeDTO typpmTeamTreeDTO = new TyppmTeamTreeDTO();
                    typpmTeamTreeDTO.setType("用户");
                    typpmTeamTreeDTO.setName(name);
                    typpmTeamTreeDTO.setFullName(name);
                    typpmTeamTreeDTO.setState(userDO.getStatus().toString());
                    typpmTeamTreeDTO.setDescription(name);
                    typpmTeamTreeDTO.setLinkID(snowflakeIdComponent.getInstance().nextId().toString());
                    typpmTeamTreeDTO.setOid(userDO.getOid().toString());
                    typpmTeamTreeDTO.setOtype("ty.inteplm.org.CTyUser");
                    typpmTeamTreeDTO.setCreatestamp(LocalDateTime.now());
                    typpmTeamTreeDTO.setUpdatestamp(LocalDateTime.now());
                    typpmTeamTreeDTO.setModifystamp(LocalDateTime.now());

                    children.add(typpmTeamTreeDTO);
                }
                projectTeamTreeDTO.setChildren(children);
            }

            for (TyppmTeamTreeDTO typpmTeamTreeDTO : projectTeamTreeDTO.getChildren()) {
                typpmTeamTreeDTO.setProjectOID(project.getOid());
                typpmTeamTreeDTO.setProjectOTYPE("ty.inteplm.project.CTyProject");
                typpmTeamTreeDTO.setLinkID(snowflakeIdComponent.getInstance().nextId().toString());
            }
            for (TyppmTeamRoleManger typpmTeamRoleManger : projectTeamTreeDTO.getTeamRoleMangers()){
                  typpmTeamRoleManger.setOid(snowflakeIdComponent.getInstance().nextId().toString());
                  typpmTeamRoleManger.setContainerOID(project.getOid());
            }
            projectTeamTreeDTO.setProjectOID(project.getOid());
            projectTeamTreeDTO.setChildName("参考创建");
            projectTeamTreeDTO.setLinkID(linkId);
            typpmProjectTeamService.insertRoleAndUserByContainerJAC(project.getOid(), "ty.inteplm.project.CTyProject", projectTeamTreeDTO);
        }
        List<TyppmPrjStakeholderDTO> prjStakeholders = project.getPrjStakeholders();
        if (prjStakeholders != null) {
            prjStakeholders.forEach((prjStakeholder) -> {
                prjStakeholder.setProjectID(project.getOid());
            });
            typpmPrjStakeholderService.batchInsertStakeholder(prjStakeholders);
        }
        //维护关联关系
        createRelation(project.getOid(),project.getContainerTeamOID());
        createRelations(project.getContainerTeamOID());
        BigInteger OID = snowflakeIdComponent.getInstance().nextId();
        BigInteger attrDefOID = new BigInteger("717552793724518400");
        String attrDefOType="ty.inteplm.attribute.CTyStringDef";
        BigInteger ObjectId = new BigInteger(safeToString(project.getOid()));
        String ObjectType = "ty.inteplm.project.CTyProject";
        String refObj="ty.zddw.kernel.CTyReferenceBase";
        String value=stringStringMap.get("PROJECT_CODE");
        String valuex = stringStringMap.get("PROJECT_CODE");
        rtProjectMapper.insertOrder(OID,attrDefOID, attrDefOType, ObjectId, ObjectType, refObj, value, valuex);
        //设置初始项目状态
        BigInteger statusOid=snowflakeIdComponent.getInstance().nextId();
        BigInteger attrDefOiD = new BigInteger("720993539668492288");
        String Value="编制中";
        rtProjectMapper.insertOrder(statusOid,attrDefOiD,attrDefOType, ObjectId, ObjectType, refObj, Value, Value);
//        更新实际时间
        updateActualDate(old,neu,new BigInteger(project.getOid()));

    }

    private void createRelations(String containerTeamOID) {
        BigInteger aoid=new BigInteger(containerTeamOID);
        String aotype="ty.inteplm.org.CTyContainerTeam";
        BigInteger createrOid=new BigInteger("105");
        BigInteger isvirtualrole=new BigInteger("1");
        String namea="INVITED";
        String nameb="orgs";
        String namec="GUEST";
        String named="teamMembers";
        String namee="pendingUsers";
        String botype="ty.inteplm.org.CTyGroup";
        String otype="ty.inteplm.org.CTyRolePrincipalMap";
        rtProjectMapper.insertRelations(aoid,aotype,getOid(),botype,createrOid,isvirtualrole,namea,getOid(),otype,namea);
        rtProjectMapper.insertRelations(aoid,aotype,getOid(),botype,createrOid,isvirtualrole,nameb,getOid(),otype,nameb);
        rtProjectMapper.insertRelations(aoid,aotype,getOid(),botype,createrOid,isvirtualrole,namec,getOid(),otype,namec);
        rtProjectMapper.insertRelations(aoid,aotype,getOid(),botype,createrOid,isvirtualrole,named,getOid(),otype,named);
        rtProjectMapper.insertRelations(aoid,aotype,getOid(),botype,createrOid,isvirtualrole,namee,getOid(),otype,namee);
    }

    private void createRelation(String prjoid, String containerTeamOID) {
        BigInteger createrOid=new BigInteger("105");
        Date createTime=new Date();
        Date updateTime=new Date();
        Date modifyTime=new Date();
        String otype="ty.inteplm.access.CTyAccessObjRight";
        String ownerString="7";
        BigInteger principalOida=new BigInteger("757953699301654535");
        BigInteger principalOidb=new BigInteger("757953699272294400");
        BigInteger principalOidc=new BigInteger("757953699272294401");
        String principalType="ty.inteplm.org.CTyGroup";
        BigInteger accessBita=new BigInteger("1");
        BigInteger accessBitb=new BigInteger("-1");
        BigInteger relobjectOida=new BigInteger(prjoid);
        BigInteger relobjectOidb=new BigInteger(containerTeamOID);
        String relobjectTypea="ty.inteplm.project.CTyProject";
        String relobjectTypeb="ty.inteplm.org.CTyContainerTeam";


        rtProjectMapper.insertRelation(otype,ownerString,principalOida,principalType,accessBita,relobjectOida,relobjectTypea,createrOid,createTime,updateTime,modifyTime,getOid());
        rtProjectMapper.insertRelation(otype,ownerString,principalOidb,principalType,accessBita,relobjectOida,relobjectTypea,createrOid,createTime,updateTime,modifyTime,getOid());
        rtProjectMapper.insertRelation(otype,ownerString,principalOidc,principalType,accessBitb,relobjectOida,relobjectTypea,createrOid,createTime,updateTime,modifyTime,getOid());
        rtProjectMapper.insertRelation(otype,ownerString,principalOida,principalType,accessBita,relobjectOidb,relobjectTypeb,createrOid,createTime,updateTime,modifyTime,getOid());
        rtProjectMapper.insertRelation(otype,ownerString,principalOidb,principalType,accessBita,relobjectOidb,relobjectTypeb,createrOid,createTime,updateTime,modifyTime,getOid());
        rtProjectMapper.insertRelation(otype,ownerString,principalOidc,principalType,accessBitb,relobjectOidb,relobjectTypeb,createrOid,createTime,updateTime,modifyTime,getOid());

    }
    private BigInteger getOid(){
        BigInteger oid = snowflakeIdComponent.getInstance().nextId();
        return oid;
    }

    @Override
    public void transJob(TransJobDTO transJobDTO) {
        //1.判断是否在同一个组里
        Map<String, Object> params = new HashMap<>();
        params.put("containeroid", transJobDTO.getProjectID());
        params.put("groupOid", transJobDTO.getExecutorGroupOid());
        params.put("usertype", "USER");
        PageInfo<TyppmUserEx> result = this.typpmPlanResourceService.queryPrjTeamUserByPrjOid(params, null, null);
        if (CollUtil.isEmpty(result.getList())){
            throw RTErrorCodeEnum.GROUP_NOT_EXIST.getException("当前组未添加成员，请设置");
        }
        boolean containsExecutor =
                result.getList().stream()
                        .map(TyppmUserEx::getOid)  // 提取用户ID
                        .anyMatch(oid -> oid != null && oid.equals(transJobDTO.getExecutorOID()));
        if (!containsExecutor) {
            throw RTErrorCodeEnum.EXECUTOR_NOT_EXIST_GROUP.getException("目标执行人不在同一组中，无法转交");
        }
        //2.更改任务的执行人
        rtProjectMapper.transExecutor(transJobDTO.getJobID(),transJobDTO.getExecutorOID());

    }

    /**
     * 判断当前登录用户的oid是否存在于组织成员列表（members）的oid中
     */
    @Override
    public boolean checkAnnualProjectAdmin() {
        UserDO user = TyAccountContext.getUser();
        List<PrincipalEntity> members = this.orgExtService.getMembersByOrg(Tools.toBigInteger(rtOrgUser));
        this.typlmUserService.dealUserLangName(members);
        members.sort(Comparator.comparing(CTyEntityBaseDO::getCreatestamp, Comparator.nullsFirst(Date::compareTo)).reversed());
        return members.stream()
                .anyMatch(member -> ObjectUtil.equal(member.getOid(),user.getOid()));
    }

    @Override
    public String getPrjDescription(String oid) {
        return rtProjectMapper.queryDescription(oid);
    }

    // 设置固定默认值
    private void setProjectFixedDefaults(TyppmProjectDTO project) {
        project.setTypeLogicalIdentifier(TableTypeConstrant.TY_PROJECT_TYPE);
        project.setAutoUpdateProject("MANUAL_SYNC_ONLY");
        project.setBudget(0.0);
        project.setRiskValue("MEDIUM");
        project.setHealthStatus("UNAVAILABLE");
        project.setContainerTreamMngState("RUNNING");
    }

    // 设置用户相关信息
    private void setUserRelatedProperties(TyppmProjectDTO project) {
        UserDO currentUser = TyAccountContext.getUser();
        project.setSponsorOID(currentUser.getOid().toString());
        project.setSponsorOTYPE(currentUser.getOtype());
        project.setCreatorOID(currentUser.getOid().toString());
        project.setCreatorOTYPE(currentUser.getOtype());
        project.setAdminUserOid(currentUser.getOid().toString());
        project.setAdminUserName(currentUser.getName());
        project.setAdminUserDescription(currentUser.getDescription());
        project.setAdminUserOtype(currentUser.getOtype());
    }

    // 设置容器和域信息
    private void setContainerAndDomainInfo(TyppmProjectDTO project) {
        project.setDomainOID("460528475");
        project.setDomainOTYPE(TableTypeConstrant.TY_DOMAIN_OTYPE);
        project.setParentDomainOID("823093347");
        project.setParentDomainOTYPE(TableTypeConstrant.TY_DOMAIN_OTYPE);
        project.setContainerTemplateOID("-1822221923446947862");
        project.setContainerTemplateOTYPE(TableTypeConstrant.TY_CONTAINERTEMPLATE_OTYPE);
    }

    // 设置类型和管理信息
    private void setTypeAndManagementInfo(TyppmProjectDTO project) {
        project.setTypeBranchid("327175971134570496");
        project.setTypeOID("327175971096821760");
    }

    // 设置更新者信息
    private void setUpdatorInfo(TyppmProjectDTO project) {
        UserDO currentUser = TyAccountContext.getUser();
        project.setUpdatorOID(currentUser.getOid().toString());
        project.setUpdatorName(currentUser.getName());
        project.setUpdatorOTYPE(currentUser.getOtype());
    }

    // 查询源项目计划
    private void setSourceProjectPlan(TyppmProjectDTO project) {
        TyppmPlan plan = typpmPlanService.queryPlanDTOByParent(
                project.getOldProjectOID(),
                TableTypeConstrant.TY_PROJECT_TYPE
        );
                project.setPlan(plan);
    }

    // 填充公共项目数据
    private void fillCommonProjectData(TyppmProjectDTO project) {
        rtProjectMapper.fillCommnNewProject(project.getOid());
        //processIbaAttributes(project);
}
    @Transactional(
            rollbackFor = {Exception.class}
    )

    /**
     * 处理项目的IBA属性
     */
    public void processIbaAttributes(TyppmProjectDTO project) {
        Map<String, List<String>> ibaMap = project.getIbaAttribute();
        if (MapUtil.isEmpty(ibaMap)) {
            List<SaveObjIBADTO> objWithIbaList=new ArrayList<>();
            IbaDTO iba1 = createIbaDto("ExecutionStatus", "编制中");
            IbaDTO iba2 = createIbaDto("CreationMethod", "Reference");
            CTyEntityBaseDO entityBase = new CTyEntityBaseDO(Tools.toBigInteger(project.getOid()), TableTypeConstrant.TY_PROJECT_TYPE);
            SaveObjIBADTO jobDto1 = createSaveObjDto(entityBase, iba1);
            SaveObjIBADTO jobDto2 = createSaveObjDto(entityBase, iba2);
            objWithIbaList.add(jobDto1);
            objWithIbaList.add(jobDto2);
            typlmAttributeValueService.batchSaveIBAByObjList(objWithIbaList, false);
            return;
        }
        List<SaveObjIBADTO> objWithIbaList = prepareIbaData(project, ibaMap);
        if (CollUtil.isNotEmpty(objWithIbaList)) {
            typlmAttributeValueService.batchSaveIBAByObjList(objWithIbaList, false);
        }
    }

    /**
     * 准备IBA数据
     */
    private List<SaveObjIBADTO> prepareIbaData(TyppmProjectDTO project, Map<String, List<String>> ibaMap) {
        List<SaveObjIBADTO> objWithIbaList = new ArrayList<>();
        BigInteger projectOid = Tools.toBigInteger(project.getOid());
        String projectOtype = TableTypeConstrant.TY_PROJECT_TYPE;
        CTyEntityBaseDO entityBase = new CTyEntityBaseDO(projectOid, projectOtype);
        // 遍历IBA属性
        ibaMap.entrySet().stream()
                .filter(entry -> CollUtil.isNotEmpty(entry.getValue()))
                .forEach(entry -> {
                    String attrKey = entry.getKey();
                    String attrValue = entry.getValue().get(0);
                    IbaDTO ibaDTO = createIbaDto(attrKey, attrValue);
                    SaveObjIBADTO saveObjDto = createSaveObjDto(entityBase, ibaDTO);
                    objWithIbaList.add(saveObjDto);
                });

        IbaDTO iba = createIbaDto("ExecutionStatus", "编制中");
        SaveObjIBADTO jobDto = createSaveObjDto(entityBase, iba);
        IbaDTO iba1 = createIbaDto("CreationMethod", "参考创建");
        SaveObjIBADTO jobDto1 = createSaveObjDto(entityBase, iba1);
        objWithIbaList.add(jobDto);
        objWithIbaList.add(jobDto1);
        return objWithIbaList;
    }

    private IbaDTO createIbaDto(String key, String value) {
        IbaDTO ibaDTO = new IbaDTO();
        ibaDTO.setKey(key);
        ibaDTO.setValue(value);
        return ibaDTO;
    }

    /**
     * 创建保存对象DTO
     */
    private SaveObjIBADTO createSaveObjDto(CTyEntityBaseDO entityBase, IbaDTO ibaDTO) {
        SaveObjIBADTO dto = new SaveObjIBADTO();
        dto.setObjIbaList(Collections.singletonList(ibaDTO));
        dto.setCTyEntityBaseDO(entityBase);
        return dto;
    }


    private boolean isExcelFile(MultipartFile file) {
        String fileName = file.getOriginalFilename();
        if (fileName == null) return false;
        return fileName.endsWith(".xlsx") || fileName.endsWith(".xls");
    }


    /**
     * 提取行数据中以指定后缀结尾的列的值
     *
     * @param rowData 单行数据
     * @param suffix 后缀（如"结束时间"）
     * @return 列名和值的映射
     */
    public static Map<String, String> extractColumnsBySuffix(Map<String, String> rowData, String suffix) {
        Map<String, String> result = new LinkedHashMap<>();
        for (Map.Entry<String, String> entry : rowData.entrySet()) {
            if (entry.getKey().endsWith(suffix)) {
                // 去掉键末尾的后缀
                String newKey = entry.getKey().substring(0, entry.getKey().length() - suffix.length());
                result.put(newKey, entry.getValue());
            }
        }
        return result;
    }


    // 判断是否为周末
    public boolean isWeekend(LocalDate date) {
        DayOfWeek day = date.getDayOfWeek();
        return day == DayOfWeek.SATURDAY || day == DayOfWeek.SUNDAY;
    }

    // 将日期调整为下一个工作日（如果当前是周末）
    public LocalDate adjustToNextWorkday(LocalDate date) {
        DayOfWeek day = date.getDayOfWeek();
        if (day == DayOfWeek.SATURDAY) {
            return date.plusDays(2); // 周六 → 周一
        } else if (day == DayOfWeek.SUNDAY) {
            return date.plusDays(1); // 周日 → 周一
        }
        return date; // 工作日不调整

    }

    @Override
    public List<String> collectCommonProjectName() {
        Set<String> result = new HashSet<>();
        IdentifierEntity identifierEntity = rtProjectMapper.selectTemplateSubFolderByName("标准项目模板");
        if (ObjectUtil.isNotNull(identifierEntity)) {
            List<EntityBase> childrenByParent = typpmProjectTemplateLibService.getChildrenByParent(
                    String.valueOf(identifierEntity.getOid()), identifierEntity.getOtype(), null);
            for (EntityBase entityBase : childrenByParent) {
                // 处理找到的实体
                if(entityBase instanceof TyppmProjectTemplate){
                    TyppmProjectTemplate template = (TyppmProjectTemplate) entityBase;
                    TyppmPlan plan = this.typpmPlanService.queryPlanDTOByParent(template.getProjectOID(), TableTypeConstrant.TY_PROJECT_TYPE);
                    PlanVO map = BeanMapper.map(plan, PlanVO.class);
                    this.fillPlanTypeInfo(map);
                    JSONObject projectTree = this.planActiveService.queryPlanActiveTreeByRootOIDAndRootOTYPE(map.getOid(), TableTypeConstrant.TY_PLAN_OTYPE, false);
                    String treeName = projectTree.getString("Name");
                    result.add(treeName);
                    JSONArray taskArray = projectTree.getJSONArray("Tasks");
                    if (CollUtil.isNotEmpty(taskArray)) {
                        for (int i = 0; i < taskArray.size(); i++) {
                            JSONObject task = taskArray.getJSONObject(i);
                            String taskName = task.getString("Name");
                            result.add(taskName);
                            JSONArray children = task.getJSONArray("children");
                            for (int j = 0; j < children.size(); j++) {
                                JSONObject child = children.getJSONObject(j);
                                String childName = child.getString("Name");
                                result.add(childName);
                                dealChild(child,result);
                            }
                        }
                    }
                }
            }
        }
        log.info("标准化项目名称：{}",result);
        return new ArrayList<>(result);
    }

    private void fillPlanTypeInfo(PlanVO planVO) {
        List<TypeVueVO> typeVueVOList = this.typlmTypeService.queryTypeByIds(Collections.singletonList(
                com.hustcad.plm.pdm.core.util.Tools.toBigInteger(planVO.getTypeOID())));
        typeVueVOList.stream().findFirst().ifPresent((typeVueVO) -> {
            planVO.setTypeDisplayName(typeVueVO.getDisplayName());
            planVO.setTypeLogicalIdentifier(typeVueVO.getLogicalidentifier());
        });
    }

    private void dealChild(JSONObject child, Set<String> result) {
        JSONArray children = child.getJSONArray("children");
        if(CollUtil.isNotEmpty( children)){
            for (int j = 0; j < children.size(); j++) {
                JSONObject childchild = children.getJSONObject(j);
                String childName = childchild.getString("Name");
                result.add(childName);
                dealChild(childchild,result);
            }
        }
    }

    @Override
    public CreateChangeInfoRequestVO getChangeInfo(PlanChangeInfoVO params) {
        planPublishCheck(params);
        queryNeedPublish( params);
        params.setChangeType("PUBLISH");
        return this.typpmChangeInfoService.createPlanChangeInfo(params);
    }

    private void planPublishCheck(PlanChangeInfoVO params) {
        List<PlusTaskDTO> plusTaskDTOS = typpmPlanActiveService.queryPlanActiveTreeByPlanOidAndOtype(
                params.getPlanoid(), TableTypeConstrant.TY_PLAN_OTYPE, new HashMap<>());
        for (PlusTaskDTO taskDTO : plusTaskDTOS) {
            checkExecutorOID(taskDTO);
        }
        String state = rtTaskMapper.setIsState(params.getProjectoid(), RTPlmConstant.EXECUTIONSTATUS);
        if (!StringUtils.equals(state, "编制中")) {
            throw RTErrorCodeEnum.PLAN_PUBLISH_START_ERROR.getException("当前项目不是编制中状态,不允许发布");
        }
        Plan plan = this.planService.selectOneByOID(new BigInteger(params.getPlanoid()));
        if (Objects.isNull(plan)) {
            TyException.throwSingleTyExceptionVarargs(ResponseCodeEnum.BIZ_OBJECT_NOT_FOUND, new String[]{"项目计划"});
        }

        if (Boolean.TRUE.equals(plan.isLocked())) {
            TyException.throwSingleTyExceptionVarargs(ResponseCodeEnum.BIZ_COMMON_OBJ_LOCK_CANNOT_OPERATE, new String[]{"项目计划[" + plan.getName() + "]", plan.getLockUserName()});
        }

        Project project = this.projectService.selectOneByOID(plan.getParentOID());
        if (Objects.isNull(project)) {
            TyException.throwSingleTyExceptionVarargs(ResponseCodeEnum.BIZ_OBJECT_NOT_FOUND, new String[]{"项目"});
        }
    }

    private void checkExecutorOID(PlusTaskDTO taskDTO) {
        // 1. 检查当前任务的executorOID
        if (ObjectUtil.isNull(taskDTO.getExecutorOID())) {
            // 可以补充任务ID等信息，方便定位问题
            throw RTErrorCodeEnum.EXECUTOR_NOT_EXIST.getException("当前计划存在执行人为空");
        }

        // 2. 检查子任务（如果有子任务）
        List<PlusTaskDTO> children = taskDTO.getChildren();
        if (CollectionUtil.isNotEmpty(children)) {
            for (PlusTaskDTO childTask : children) {
                // 递归检查子任务
                checkExecutorOID(childTask);
            }
        }
    }

    private void queryNeedPublish(PlanChangeInfoVO params) {
        List<PlanActive> planActives = this.planActiveOperateService.selectChildActive(Tools.toBigInteger(params.getRefoid()), params.getRefotype());
        if (CollectionUtils.isEmpty(planActives)) {
            throw WfbSystemErrorCodeEnum.ERROR_START_PROCESS_CUSTOM_MESSAGE.getException("项目计划中无可发布的计划");
        } else {
            JSONArray array = JSONArray.parseArray(JSON.toJSONString(planActives));
            JSONObject parse = new JSONObject();
            parse.put("data", array);
            params.setPlanActiveDTO(parse);
            List<ActiveUpdateVO> activeList = planActives.stream().map((active) -> {
                ActiveUpdateVO vo = new ActiveUpdateVO();
                vo.setOid(Tools.toString(active.getOid()));
                vo.setOtype(active.getOtype());
                return vo;
            }).collect(Collectors.toList());
            params.setActiveList(activeList);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void synchronizationParentJobOutRules(JSONObject params) {
        String parentJobId = params.getString("parentJobId");
        PageInfo<TyppmOutRuleDTO> result = this.typpmOutRuleService.queryOutRuleByPage(parentJobId, "", 1, 999);
        List<TyppmOutRuleDTO> allParentJobOutRuleS = result.getList();
        String currentJobId = params.getString("currentJobId");

        List<String> parentOutRulesNameList = allParentJobOutRuleS.stream().map(TyppmOutRuleDTO::getName).collect(Collectors.toList());
        PageInfo<TyppmOutRuleDTO> currentJobOutRuleS = this.typpmOutRuleService.queryOutRuleByPage(currentJobId, "", 1, 999);
        List<TyppmOutRuleDTO> currentJobOutRuleList = currentJobOutRuleS.getList();
        if(CollUtil.isNotEmpty(currentJobOutRuleList)){
            for (TyppmOutRuleDTO typpmOutRuleDTO : currentJobOutRuleList) {
                if(parentOutRulesNameList.contains(typpmOutRuleDTO.getName())){
                   // throw RTErrorCodeEnum.PLAN_PUBLISH_START_ERROR.getException("当前计划存在相同的交付物规则规则:"+typpmOutRuleDTO.getName());
                    throw RTErrorCodeEnum.PLAN_OUTRULE_SAME.getException("当前计划存在相同的交付物规则规则");
                }
            }
        }

        JSONArray outRules = params.getJSONArray("needSynOutRules");
        List<String> jobOutRulesIds = new ArrayList<>();
        for (int i = 0; i < outRules.size(); i++) {
            JSONObject outRuleJSON = outRules.getJSONObject(i);
            jobOutRulesIds.add(outRuleJSON.getString("oid"));
        }

        Map<String, TyppmOutRuleDTO> allJobOutRuleMap = allParentJobOutRuleS.stream()
                .collect(Collectors.toMap(
                        TyppmOutRuleDTO::getOid,
                        Function.identity(),
                        (existing, replacement) -> existing
                ));

        for (String outRulesId : jobOutRulesIds) {
            if(allJobOutRuleMap.containsKey(outRulesId)){
                TyppmOutRuleDTO parentOutRule = allJobOutRuleMap.get(outRulesId);

                OutRuleParamVO outRuleVO = new OutRuleParamVO();
                OutRuleVO outRule = buildOutRuleVO(parentOutRule, currentJobId);
                outRuleVO.setOutRule(outRule);

                List<OutRuleItemVO> outRuleItemList = new ArrayList<>();
                buildOutRuleItemVOList(parentOutRule, outRuleItemList);
                outRuleVO.setOutRuleItemList(outRuleItemList);

                this.typpmOutRuleService.insertOutRule(outRuleVO);
            }
        }
    }

    @Override
    public Map<String, Object> selectDetsMeasures(String code) {
        return rtProjectMapper.selectDetsMeasures(code);
    }

    @Override
    public String selectProjectNameByProjectCode(String projectNo) {
        return rtProjectMapper.selectProjectNameByProjectCode(projectNo);
    }

    private void buildOutRuleItemVOList(TyppmOutRuleDTO parentOutRule, List<OutRuleItemVO> outRuleItemList) {
        String ruleDisplayName = parentOutRule.getRuleDisplayName();
        List<EnumerationItemTreeVO> enumerationItemTreeList = this.typpmOutRuleService.queryRuleAttributes(
                parentOutRule.getRuletypeOid());
        Map<String, EnumerationItemTreeVO> enumerationItemTreeMap = enumerationItemTreeList.stream()
                .collect(Collectors.toMap(
                        EnumerationItemTreeVO::getDisplayName,
                        Function.identity(),
                        (v1, v2) -> v1
                ));

        if(StringUtils.isNotEmpty(ruleDisplayName) && StringUtils.isNotBlank(ruleDisplayName)){
            for (String attrAndValue : ruleDisplayName.split(";")) {
                String[] rule = attrAndValue.split(":");
                String key = rule[0];
                String value = rule[1];
                if(enumerationItemTreeMap.containsKey(key)){
                    OutRuleItemVO outRuleItemVO = new OutRuleItemVO();
                    EnumerationItemTreeVO enumerationItemTreeVO = enumerationItemTreeMap.get(key);
                    outRuleItemVO.setAttributeOid(enumerationItemTreeVO.getId());
                    outRuleItemVO.setAttributeOtype(enumerationItemTreeVO.getOtype());
                    for (EnumerationItemTreeVO child : enumerationItemTreeVO.getChildren()) {
                        if(child.getDisplayName().equals(value)){
                            outRuleItemVO.setAttributeValueOid(child.getId());
                            outRuleItemVO.setAttributeValueOtype(child.getOtype());
                            outRuleItemList.add(outRuleItemVO);
                        }
                    }
                }
            }
        }
    }

    private static OutRuleVO buildOutRuleVO(TyppmOutRuleDTO parentOutRule, String currentJobId) {
        OutRuleVO outRule = new OutRuleVO();
        String ruleTypeOid = parentOutRule.getRuletypeOid();
        String ruleTypeOtype = parentOutRule.getRuletypeOtype();
        String name = parentOutRule.getName();
        Long checkDelivers = parentOutRule.getCheckDelivers();

        outRule.setRuletypeOid(ruleTypeOid);
        outRule.setRuletypeOtype(ruleTypeOtype);
        outRule.setName(name);
        outRule.setCheckDelivers(checkDelivers);
        outRule.setJobOid(currentJobId);
        return outRule;
    }
    public String convertDateTimeFormat(String dateTimeStr) {
        if (dateTimeStr == null || dateTimeStr.trim().isEmpty()) {
            return dateTimeStr;
        }

        // 判断是否符合 "2025-09-26T00:10:24" 格式
        if (dateTimeStr.matches("\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}")) {
            // 将 T 替换为空格
            return dateTimeStr.replace('T', ' ');
        }

        return dateTimeStr;
    }

    @Override
    @Transactional(
            rollbackFor = Exception.class
    )
    public CreateChangeInfoRequestVO directlyPublishPlan(PlanChangeInfoVO params) {
        BigInteger objectOid= Tools.toBigInteger(params.getRefoid());
        String objectOtype = params.getRefotype();
        TyppmPlanActivePublishDTO typpmPlanActivePublishDTO = new TyppmPlanActivePublishDTO();
        typpmPlanActivePublishDTO.setOid(Tools.toString(objectOid));
        typpmPlanActivePublishDTO.setOtype(objectOtype);
        typpmPlanActivePublishDTO.setWorkflowSwitch(Boolean.FALSE);
        List<TyppmPlanActive> typpmplanActives = new ArrayList<>();
        List<PlanActive> planActives;
        if (TableTypeConstrant.TY_PLAN_ACTIVITY_OTYPE.equals(objectOtype)) {
            planActives = this.planActiveService2.selectChildActiveByID(objectOid, ProjectMngConstant.ACTIVE_STATUS_RELEASE);
            typpmplanActives = this.planActiveConverter.transList(planActives);
        } else if (TableTypeConstrant.TY_PLAN_OTYPE.equals(objectOtype)) {
            planActives = this.planActiveService2.selectActiveByHead(objectOid, ProjectMngConstant.ACTIVE_STATUS_RELEASE);
            typpmplanActives = this.planActiveConverter.transList(planActives);
        }

        this.typpmRemotePdmService.planActivePublish(typpmPlanActivePublishDTO);
        if (!CollectionUtils.isEmpty(typpmplanActives)) {
            this.sendMessageAfterPublish(typpmplanActives);
        }
        return null;
    }

    private void sendMessageAfterPublish(List<TyppmPlanActive> typpmPlanActiveList) {
        if ("Y".equals(this.pdmMsgSwitch) && !CollectionUtils.isEmpty(typpmPlanActiveList)) {
            List<TyppmPlanActive> planActiveList = typpmPlanActiveList.stream().filter((typpmPlanActive) -> !ProjectMngConstant.ACTIVE_STATUS_RELEASE.equals(typpmPlanActive.getExecutedStatus())).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(planActiveList)) {
                String nameKey = "projectPublish";
                TyppmMessageTemplateDefine templateDefine = this.typpmMessageTemplateDefineService.getMessageTemplateDefineByKey(nameKey);
                if (!Objects.isNull(templateDefine)) {
                    String projectOid = planActiveList.get(0).getContainerOID();
                    TyppmProject typpmProject = this.typpmProjectService.selectOneByOID(projectOid);
                    List<TyppmUser> projectManagerList = this.typpmProjectTeamService.queryProjectManagerList(typpmProject.getOid());
                    String projectManager = "";
                    if (!CollectionUtils.isEmpty(projectManagerList)) {
                        projectManager = projectManagerList.stream().map((userx) -> userx.getFullName() + "(" + userx.getName() + ")").collect(Collectors.joining("，"));
                    }

                    String senderOid = "";
                    if (CharSequenceUtil.isNotBlank(this.senderUser)) {
                        TyppmUser user = this.typpmUserService.checkUserRemote(this.senderUser);
                        if (Objects.nonNull(user)) {
                            senderOid = user.getOid();
                        }
                    }

                    List<BatchRemoteMessageParams> messageParamList = new ArrayList<>();
                    Iterator<TyppmPlanActive> var11 = planActiveList.iterator();

                    while(var11.hasNext()) {
                        TyppmPlanActive planActive = var11.next();
                        String msgName = templateDefine.getTemplateTitle().replace("project.name", typpmProject.getName());
                        String contextTemp = templateDefine.getTemplateContext();
                        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                        String msgDetail = contextTemp.replace("project.name", typpmProject.getName()).replace("projectManager", projectManager).replace("active.name", planActive.getName()).replace("job.finishdate", planActive.getFinishDate().format(formatter)).replace("curTime", LocalDateTime.now().format(formatter)).replace("%n", "\n");
                        List<String> receiverOidList = new ArrayList<>();
                        receiverOidList.add(planActive.getOwnerOID());
                        BatchRemoteMessageDTO batchRemoteMessageDTO = new BatchRemoteMessageDTO(receiverOidList, msgName, msgDetail, senderOid);
                        BatchRemoteMessageParams messageParams = new BatchRemoteMessageParams();
                        messageParams.setMessage(batchRemoteMessageDTO);
                        messageParamList.add(messageParams);
                    }

                    this.typpmRemoteMessageHandle.batchSendMessageByParams(messageParamList);
                }
            }
        }
    }

    @Override
    public List<String> selectAllProjectIdentifier(String key) {
        if(StringUtils.isNotEmpty(key) && StringUtils.isNotBlank(key)){
            String[] split = key.split(" ");
            List<String> keys = new ArrayList<>(Arrays.asList(split));
            return this.rtProjectMapper.selectAllProjectIdentifier(keys);
        }else {
            return this.rtProjectMapper.selectAllProjectIdentifier(null);
        }
    }

    @Override
    public String selectSecurity(String oid) {
        BigInteger attfOid=new BigInteger("745823501189808128");
        String  security=rtProjectMapper.selectSecurity(oid,attfOid);
        return security;
    }
    public static long calculateDiff(LocalDate date1, LocalDate date2) {
        // 转换为固定天数：年×360 + 月×30 + 日
        long days1 = date1.getYear() * 360L + date1.getMonthValue() * 30L + date1.getDayOfMonth();
        long days2 = date2.getYear() * 360L + date2.getMonthValue() * 30L + date2.getDayOfMonth();
        return days1 - days2;
    }

    public void updateActualDate(LocalDate old,LocalDate neu,BigInteger projectOid){
        // 判断两个日期谁大
        boolean isAfter = neu.isAfter(old);
        long dayDiff = ChronoUnit.DAYS.between(old, neu);
        TyppmCalendar calendar = typpmCalendarService.getCalendarByProjectOid(
                projectOid.toString(), "ty.inteplm.project.CTyProject");
        String calendarOid = calendar.getOid();
        //查询所有链接
        List<BigInteger> bOid=rtProjectMapper.selectCalendarLink(calendarOid);
        List<CalendarVO> calendarVOS=new ArrayList<>();
        for (BigInteger bigInteger:bOid){
            CalendarVO calendarVO=rtProjectMapper.selectCalendarInfoByOid(bigInteger);
            rtProjectMapper.updateCalendar(calendarVO.getOid(),
                                           calendarVO.getStartDate().plusDays(dayDiff),
                                           calendarVO.getEndDate().plusDays(dayDiff));
        }

        TyppmPlan typpmPlan=rtProjectMapper.selectPlanByProjectId(projectOid);
        String oidPlan = typpmPlan.getOid();
        List<TyppmPlanActive> typpmPlanActiveList=rtProjectMapper.selectPlanActiveByProjectId(new BigInteger(oidPlan));
        LocalDateTime startDatePlan = typpmPlan.getStartDate().plusDays(dayDiff);
        LocalDateTime finishDatePlan = typpmPlan.getFinishDate().plusDays(dayDiff);
        rtProjectMapper.updatePlanDate(oidPlan,startDatePlan,finishDatePlan);
        for (TyppmPlanActive typpmPlanActive:typpmPlanActiveList){
            String oid = typpmPlanActive.getOid();
            LocalDateTime startDate = typpmPlanActive.getStartDate().plusDays(dayDiff);
            LocalDateTime finishDate = typpmPlanActive.getFinishDate().plusDays(dayDiff);
            LocalDateTime earlyStart = typpmPlanActive.getEarlyStart().plusDays(dayDiff);
            LocalDateTime earlyFinish = typpmPlanActive.getEarlyFinish().plusDays(dayDiff);
            LocalDateTime lateStart = typpmPlanActive.getLateStart().plusDays(dayDiff);
            LocalDateTime lateFinish = typpmPlanActive.getLateFinish().plusDays(dayDiff);

            rtProjectMapper.updateDate(oid,startDate,finishDate,earlyStart,earlyFinish,lateStart,lateFinish);
            if (typpmPlanActive.getConstraintDate()!=null){
                LocalDateTime constraintDate = typpmPlanActive.getConstraintDate().plusDays(dayDiff);
                rtProjectMapper.updateConstraintDate(oid,constraintDate);
            }

        }
        List<BigInteger> jobList = rtProjectMapper.selectJobByOID(projectOid.toString());
        for (BigInteger jobOid:jobList){
            Job job = rtProjectMapper.selectStageJob(jobOid);
            LocalDateTime startDate = job.getStartdate().toInstant().atZone(ZoneId.systemDefault()) // 绑定系统默认时区
                    .toLocalDateTime(); // 提取LocalDateTime
            LocalDateTime finishDate = job.getFinishdate().toInstant().atZone(ZoneId.systemDefault()) // 绑定系统默认时区
                    .toLocalDateTime(); // 提取LocalDateTime
            LocalDateTime startDateJob = startDate.plusDays(dayDiff);
            LocalDateTime finishDateJob = finishDate.plusDays(dayDiff);
            rtProjectMapper.updateJobDate(jobOid,startDateJob,finishDateJob);
        }


    }


    /*
     * 创建计划变更单
     */
    @Override
    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
    public CreateChangeInfoRequestVO createPlanChangeForm(JSONObject requestVO) {
        if (Objects.isNull(requestVO.getString("projectOid"))) {
            throw com.hustcad.plm.pdm.core.handle.exception.SystemErrorCodeEnum.ARGUMENT_NONE.message("projectOid");
        } else if (Objects.isNull(requestVO.getString("remark"))) {
            throw com.hustcad.plm.pdm.core.handle.exception.SystemErrorCodeEnum.ARGUMENT_NONE.message("remark");
        } else {
            String projectOid = requestVO.getString("projectOid");
            PlanChangeInfoVO planChangeInfoVO = new PlanChangeInfoVO();
            planChangeInfoVO.setDescription("项目"+requestVO.getString("remark"));
            planChangeInfoVO.setChangeNo("XMBG-" + System.currentTimeMillis());
            planChangeInfoVO.setIsSubmit(Boolean.TRUE);
            planChangeInfoVO.setProjectoid(projectOid);
            planChangeInfoVO.setProjectotype(TableTypeConstrant.TY_PROJECT_TYPE);
            TyppmPlan plan = this.typpmPlanService.queryPlanDTOByParent(projectOid, TableTypeConstrant.TY_PROJECT_TYPE);
            planChangeInfoVO.setPlanoid(plan.getOid());
            planChangeInfoVO.setPlanotype(plan.getOtype());
            PlanUpdateVO planUpdateVO = new PlanUpdateVO();
            planUpdateVO.setPlanFinishDate(String.valueOf(plan.getFinishDate()));
            planUpdateVO.setPlanStartDate(String.valueOf(plan.getStartDate()));
            planChangeInfoVO.setContent(planUpdateVO);
            JSONObject planActiveTree = this.planActiveService.queryPlanActiveTreeByRootOIDAndRootOTYPE(plan.getOid(), plan.getOtype(), true);
            JSONObject planActiveDTO = new JSONObject();

            JSONArray tasks = planActiveTree.getJSONArray("Tasks");
            List<PlusTaskSaveDTO> resultData = new ArrayList<>();
            collectAllPlanTask(resultData,tasks);
            planActiveDTO.put("data", resultData);
            planActiveDTO.put("planOid", plan.getOid());
            planActiveDTO.put("planOtype", plan.getOtype());
            planChangeInfoVO.setPlanActiveDTO(planActiveDTO);
            return this.typpmChangeInfoService.createPlanChangeInfo(planChangeInfoVO);
        }
    }

    private void collectAllPlanTask(List<PlusTaskSaveDTO> resultData, JSONArray currentPlanTasks) {
        for (int i = 0; i < currentPlanTasks.size(); i++) {
            JSONObject plan = currentPlanTasks.getJSONObject(i);
            PlusTaskSaveDTO plusTaskSaveDTO = JSONObject.parseObject(String.valueOf(plan), PlusTaskSaveDTO.class);
            plusTaskSaveDTO.setLineNumber(plusTaskSaveDTO.getOutLineLevel());
            plusTaskSaveDTO.setEarlyStart(formatDateTime(plusTaskSaveDTO.getEarlyStart()));
            plusTaskSaveDTO.setEarlyFinish(formatDateTime(plusTaskSaveDTO.getEarlyFinish()));
            plusTaskSaveDTO.setLateFinish(formatDateTime(plusTaskSaveDTO.getLateFinish()));
            plusTaskSaveDTO.setLateStart(formatDateTime(plusTaskSaveDTO.getLateStart()));
            plusTaskSaveDTO.setStartDate(formatDateTime(plusTaskSaveDTO.getStartDate()));
            plusTaskSaveDTO.setFinishDate(formatDateTime(plusTaskSaveDTO.getFinishDate()));
            plusTaskSaveDTO.setConstraintDate(formatDateTime(plusTaskSaveDTO.getConstraintDate()));
            plusTaskSaveDTO.setActualFinishDate(formatDateTime(plusTaskSaveDTO.getActualFinishDate()));
            plusTaskSaveDTO.setActualStartDate(formatDateTime(plusTaskSaveDTO.getActualStartDate()));
            plusTaskSaveDTO.setActualStartDate(formatDateTime(plusTaskSaveDTO.getActualStartDate()));
            resultData.add(plusTaskSaveDTO);
            JSONArray children = plan.getJSONArray("children");
            if (CollUtil.isNotEmpty(children)) {
                collectAllPlanTask(resultData, children);
            }
        }
    }

    private static String formatDateTime(String dateTimeStr) {
        if (StringUtils.isEmpty(dateTimeStr)) {
            return dateTimeStr;
        }
        return dateTimeStr.replace("T", " ").replaceAll("\\.\\d+", "");
    }
    private void addMainDesigner(String projectoid,TyppmTeamTreeDTO typpmTeamTreeDTO){
        TyppmTeamTreeDTO teamTreeDTO=new TyppmTeamTreeDTO();
        teamTreeDTO.setDataFlag("1");
        teamTreeDTO.setName("主设计师");
        teamTreeDTO.setTag("主设计师");
        teamTreeDTO.setType("主设计师");
        BigInteger newoid=rtProjectMapper.selectGroup("主设计师",new BigInteger(projectoid));
        String oid = snowflakeIdComponent.getInstance().nextId().toString();
        if (newoid!=null){
            oid=newoid.toString();
        }
        teamTreeDTO.setOid(oid);
        String projectType="ty.inteplm.project.CTyProject";
        List<ProjectTeamTreeDTO> projectTeamTreeDTOS = typpmProjectTeamService.queryContainerTeamByContainer(projectoid,
                                                                                                             projectType);

        String linKid = snowflakeIdComponent.getInstance().nextId().toString();
        if (CollUtil.isNotEmpty(projectTeamTreeDTOS)){
            linKid = projectTeamTreeDTOS.get(0).getLinkID();
        }
        teamTreeDTO.setDisabled(false);
        teamTreeDTO.setLinkID(linKid);
        teamTreeDTO.setType("主设计师");
        teamTreeDTO.setOtype("ty.inteplm.org.CTyGroup");
        teamTreeDTO.setChildren(Collections.singletonList(typpmTeamTreeDTO));
        teamTreeDTO.setChildName("参考创建");
        teamTreeDTO.setProjectOID(projectoid);
        teamTreeDTO.setProjectOTYPE(projectType);
        typpmProjectTeamService.insertRoleAndUserByContainerJAC(projectoid,projectType,teamTreeDTO);
    }
}
